一、Java基础

1、 Java中的基本数据类型有哪些?

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

2、JDK、JRE的区别是什么?

JDK是Java开发工具包,是提供给Java开发人员使用的

JRE是运行时环境,Java运行时需要的依赖环境

JDK中包含有JRE,若需要开发则需要安装JDK,若只需要运行Java程序,安装JRE即可

3、什么时值传递和引用传递?

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

对象被引用传递,意味着传递的并不是实际的对象,而是对象的应用,所以,外部对引用对象所做的改变会影响到所有对象上。

4、接口和抽象类的区别是什么?

接口是绝对抽象的,不可以被实例化。抽象类也不可被实例化,但其包含main方法是可以被调用的。

接口中声明的变量都是被final修饰的。抽象类中可以包含非final的变量。

接口中成员函数默认都是public的,因为需要外部能够访问接口。抽象类中的成员函数可以是private、protected、或者是public。

接口可以多实现,抽象类只能单继承

类实现一个接口,需要实现接口声明的所有方法。

类继承一个抽象类,可以不实现抽象类声明的所有方法,前提这个类要声明成抽象类

5、final、finally、finalize的区别?

final:修饰符(关键字)有三种用法:①修饰类:意味此类不能再派生子类,即不能被继承。②修饰变量:使变量在赋值一次之后不可变更。③修饰方法:表示此方法在子类中不可被重写。

finally:通常放在try···catch的后面构造总是执行代码块,意味着程序无论是否正常运行,这里的代码只要JVM不关闭都能执行。

finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

6、说说&和&&的区别?

&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

&&还具有短路功能,即如果第一个表达式为false,则不再计算第二个表达式。

&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f 的结果为 0x01。

7、存在使i+1<i的数吗?

存在

如果i为int类型,那么当i为int能表示的最大整数时,i+1就溢出变成负数了,此时就<i了

8、int和Integer的区别?

Java为了能够将基本数据类型当成对象来操作,Java为每个基本数据类型都引入了对应的包装类型,int的包装类型就是Integer,从JDK1.5开始引入自动装箱/拆箱机制,使得二者之间可以相互转换。

9、String和StringBuilder、StringBuffer的区别?

String 的长度是不可变的;

StringBuffer的长度是可变的,如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用 String Buffer,如果最后需要 String,那么使用 StringBuffer 的 toString() 方法;线程安全;

StringBuilder 是从 JDK 5 开始,为StringBuffer该类补充了一个单个线程使用的等价类;通常应该优先使用 St ringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

10、什么是Java序列化,如何实现Java序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,只是为了标注该对象是可以被序列化的,然后使用一个输出流来构造一个ObjectOutputStream对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

11、HashMap和Hashtable有什么区别?

HashMap 和 Hashtable 都实现了 Map 接口,因此很多特性非常相似。但是,他们有以下不同点: HashMap 允许键和值是 null,而 Hashtable 不允许键或者值是 null。

Hashtable 是同步的,而 HashMap 不是。因此, HashMap 更适合于单线程环境,而 Hashtable 适合于多线 程环境。 HashMap 提供了可供应用迭代的键的集合,因此,HashMap 是快速失败的。另一方面,Hashtable 提供了对 键的列举(Enumeration)。

一般认为 Hashtable 是一个遗留的类。

12、ArrayList和LinkedList有什么区别?

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

ArrayList底层采用动态数组结构,在堆内存中有一段连续的独立空间,所以ArrayList的查询非常快,一般情况下,由于插入数据的时候需要对其他数组元素进行移动操作,所以插入效率低。但可以使用尾插法来提高ArrayList的插入效率,因为在尾部插入数据不需要移动其他数据。效率甚至可以超过LinkedList。

LinkedList底层采用链表结构,在内存中零散分布,相互之间只记录前一个和后一个元素。插入数据时不需要移动其他元素位置,所以LinkedList插入效率相对较高。LinkedList 比 ArrayList 更占内存,因为 LinkedList 为每一个节点存储了两个引用,一个指向前一个元素,一 个指向下一个元素。

13、创建线程的方式有哪些?

• 继承 Thread 类

• 实现 Runnable 接口

• 应用程序可以使用 Executor 框架来创建线程池

14、线程的几种可用状态?

线程在执行过程中,可以处于下面几种状态:

• 就绪(Runnable):线程准备运行,不一定立马就能开始执行。

• 运行中(Running):进程正在执行线程的代码。

• 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。

• 睡眠中(Sleeping):线程被强制睡眠。

• I/O阻塞(Blocked on I/O):等待I/O操作完成。

        同步阻塞(Blocked on Synchronization):等待获取锁。

• 死亡(Dead):线程完成了执行。

15、sleep()和wait()有什么区别?

sleep()方法是线程类Thread的静态方法,调用sleep方法使此线程暂停执行指定时间,将执行的机会给其他线程,但是监控状态依然保持,到时间后自动恢复(就绪状态),此过程中不会释放对象锁。

wait()是Object类的方法,调用wait方法使此线程放弃对象锁的竞争,进入等待,只有针对此对象发出notify(或者notifyAll)后此线程才会进入锁池来竞争锁。

16、Java中HashMap的工作原理是什么?

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,他使用hashCode()和equals()方法来向集合/从集合添加检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在,value会被更新成新值。HashMap的一些重要的特性是它的容器,负载因子和扩容极限。

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

Java中HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法,如果没有正确实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且这两个方法也用来发现重复元素,所以这两个元素的实现对HashMap的精确性和正确性是至关重要的。

18、HashSet和TreeSet有什么区别?

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

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

19、解释内存中的栈(stack)、堆(heap)和静态存储区的用法

通常我们定义一个基本数据类型的变量,一个对象的应用,还有就是函数调用的现场保持都是使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序的字面量如直接书写的100、“hello”和常量都是放在静态存储区中。栈空间操作最快但是很小,通常大量的对象都是放在堆空间 的,整个内存包括硬盘上的虚拟内存都可以被当成堆空间来使用。

20、什么是死锁?以及如何避免死锁?

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

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了