一边盘点之前的知识点,一边总结一些常见的面试题吧... ...
1、JDK 和 JRE 有什么区别?
JDK(Java SE Development Kit),Java标准的开发包,提供了编译、运行Java程序所需要的各种工具和资源,包括了Java编译器、Java运行时环境、以及常用的Java类库等。 JRE(Java Runtime Environment),Java运行时环境,用于解释执行Java的字节码文件。普通用户只需要安装JRE来运行Java程序即可,JDk包含JRE,JDK 和 JRE 中都包含 JVM。 |
2.== 和 equals 的区别是什么?
==:比较两个对象的堆内存地址,如果相等,则说明指向的是同一个对象地址的。 对于基本数据类型(byte,short,char,int,float,double,long,boolean)来说,他们是作为常量在方法区中的常量池里面以HashSet策略存储起来的,在常量池中,一个常量只会对应一个地址。 另外,对于基本数据的包装类型(Byte, Short, Character,Integer,Float, Double,Long, Boolean)除了Float和Double之外,其他的六种都是实现了常量池的。 equals:用来比较字符串和对象数据类型(在Object类下),我们也可以重写equals方法进行对象值的比较。 |
3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
1.hashCode()相等的两个对象他们的equal()不一定相等。 因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠。 2.equal()相等的两个对象他们的hashCode()肯定相等。 所有对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低。所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性。 |
4.final 在 java 中有什么作用?
Final修饰类 Final修饰一个类时表示这个类不能被继承,此时类中的所有方法都被隐式的指定为final方法。所以修饰类要谨慎使用。 Final修饰方法 使用final修饰方法的作用有两个:一是把方法锁定,防止任何子类继承它以后修改方法的内容;二是效率,早期的java会把final方法转为内嵌使用,但如果方法过于庞大的话,效率提升的效果也不是很明显。 Final修饰变量 当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。 final修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。 |
5.java 中的 Math.round(-1.5) 等于多少?
Round()方法是向下取整,也就是+0.5再四舍五入,-1.5+0.5=-1 |
6.String 属于基础的数据类型吗?
String属于引用数据类型 |
7.java 中操作字符串都有哪些类?它们之间有什么区别?
String、StringBuffer、StringBuilder: ①这三种都是以char[ ]形式保存字符串的; ②String类型的字符串是不可变的,如果想要修改,只能new的方式新创建一个字符串; ③StringBuffer和StringBuilder是可变的字符串,可进行增删操作; ④StringBuffer中的大部分方法用synchronized修饰,是线程安全的; ⑤StringBuilder是线程不安全的,但是效率高。如果是单线程,使用StringBuilder,多线程使用StringBuffer。 |
8.String str="i"与 String str=new String(“i”)一样吗?
不一样。String str=”i”,意思是将i这个值在内存中的地址附给str,而String str1=new String(“i”)的意思是新建一个对象,将这个对象的地址附给str,因为String 是final的,所以”i”应该是放在常量池中。但是此时str1.equals(str)的结果是true。 |
9.如何将字符串反转?(假设我们给定的字符串为str)
①利用StringBuffer或者StringBuilder中的reverse成员方法; Public static String reverse1(String str){ Return new StringBuilder(str).reverse().toString(); } ②利用String的toCharArray()将字符串str先转换成char数组,然后将各个字符串拼接 Public static String reverse2(String str){ Char[ ] chars=str.toCharArray(); String reverses=” “; For(int i=chars.length-1;i>=0;i--){ Reverses+=chars[i]; } Return reverses; } ③利用String的charAt()取出字符串的每一个字符; Public static String reverse3(String str){ String reverses=” “; For(int i=0;i<str.length;i++){ Reverses=str.charAt(i)+reverses; } Return reverses; } |
10.String 类的常用方法都有那些?
1>length():得到一个字符串的字符个数/长度 2>getByte():将一个字符串转换成字节数组 3>toCharArray():将一个字符串转换成字符数组 4>split(String): 将一个字符串按照指定内容劈开 5>equals():判断两个字符串的内容是否一样 6>equalslsIgnoreCase(String): 忽略太小写的比较两个字符串的内容是否一样 7>contains(String): 判断一个字符串里面是否包含指定的内容 8>startsWith(String): 判断一个字符串是否以指定的内容开头 9>endsWith(String): 判断一个字符串是否以指定的内容结尾 10>toUpperCase():将一个字符串全部转换成大写 11>toLowerCase():将一个字符串全部转换成小写 12>replace(String,String): 将某个内容全部替换成指定内容 13>replaceAll(String,String): 将某个内容全部替换成指定内容,支持正则 14>replaceFirst(String,String): 将第一次出现的某个内容替换成指定的内容 15>substring(int): 从指定下标开始一直截取到字符串的最后 16>substring(int,int): 从下标x截取到下标y-1对应的元素 17>trim():去除一个字符串的前后空格 18>charAt(int): 得到指定下标位置对应的字符 19>indexOf(String): 得到指定内容第一次出现的下标 20>laseIndexOf(String): 得到指定内容最后一次出现的下标 |
11.抽象类必须要有抽象方法吗?
不一定。用abstract关键字修饰的类是抽象类,抽象类可以没有抽象方法,但是如果一个类包含抽象方法,那么这个类一定是抽象类。抽象类是没有办法实例化的,只能通过其子类实例化。 |
12.普通类和抽象类有哪些区别?
①抽象类被关键字abstract修饰,不能被实例化; ②抽象类可以有抽象方法,可以不必实现,只需声明; ③含有抽象方法的类一定是抽象类; ④抽象类的子类必须实现抽象类的所有抽象方法,否则此子类也是抽象方法; ⑤抽象方法不能被private/final/static修饰; |
13.抽象类能使用 final 修饰吗?
不能,用final修饰的类不能被继承和修改的,而抽象类的目的就是被继承然后重写其抽象方法。 |
14.接口和抽象类有什么区别?
①抽象类是被子类继承,接口是被类实现; ②抽象类里面可以声明方法,也可以实现方法,而接口只是声明方法; ③接口里面定义的变量只能是公共的静态的常量,抽象类里面的变量是普通的变量; ④接口是设计的结果,抽象类是重构的结果; ⑤抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高; ⑥抽象类主要用来抽象类别,而接口用来抽象功能; |
15.java 中 IO 流分为几种?
①按照流的流向分,可以分为输入流和输出流; ②按照操作单元分,可以分为字节流和字符流; ③按照角色可以分为节点流和处理流 Reader:字符读取 Writer:字符写入 InputStream:字节流写入 OutputStream:字节流写出 |
16.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操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。 |
17.Files的常用方法都有哪些?
Files.exists():检测文件路径是否存在。 Files.createFile():创建文件。 Files.createDirectory():创建文件夹。 Files.delete():删除一个文件或目录。 Files.copy():复制文件。 Files.move():移动文件。 Files.size():查看文件个数。 Files.read():读取文件。 Files.write():写入文件。 |
18.同步/异步,阻塞/非阻塞分别是什么?
说法一: 同步/异步关注的是消息通信机制 (synchronous communication/ asynchronous communication) 。 所谓同步,就是在发出一个调用时,在没有得到结果之前, 该调用就不返回。 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果 阻塞/非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态. 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程 说法二: 进程间的通信是通过 send() 和 receive() 两种基本操作完成的。具体如何实现这两种基础操作,存在着不同的设计。 消息的传递有可能是**阻塞的**或**非阻塞的** -- 也被称为**同步**或**异步**的: 阻塞式发送(blocking send). 发送方进程会被一直阻塞, 直到消息被接受方进程收到。 非阻塞式发送(nonblocking send)。 发送方进程调用 send() 后, 立即就可以其他操作。 阻塞式接收(blocking receive) 接收方调用 receive() 后一直阻塞, 直到消息到达可用。 非阻塞式接受(nonblocking receive) 接收方调用 receive() 函数后, 要么得到一个有效的结果, 要么得到一个空值, 即不会被阻塞。 上述不同类型的发送方式和不同类型的接收方式,可以自由组合。 结合两种说法,自行理解同步/异步以及阻塞/非阻塞的区分。 |
19.java 容器都有哪些?
![]() |
20.Collection 和 Collections 有什么区别?
Java.util.Collection为集合接口,他提供了对集合对象进行操作的抽象的方法,为各种具体的集合提供了统一的操作。 Java.util.Collections是一个包装类,提供了大量的静态方法(对集合的搜索,排、线程安全化等),大多数方法用来处理线性表的。 |
21.List、Set、Map 之间的区别是什么?
HashSet基于哈希表实现,有以下特点: 1.不允许重复 2.允许值为null,但是只能有一个 3.无序的。 4.没有索引,所以不包含索引操作的方法 LinkedHashSet跟HashSet一样都是基于哈希表实现。只不过linkedHashSet在hashSet的基础上多了一个链表,这个链表就是用来维护容器中每个元素的顺序的。有以下特点: 1.不允许重复 2.允许值为null,但是只能有一个 3.有序的。 4.没有索引,所以不包含索引操作的方法 ArrayList是基于数组实现的,其特点是查询快,增删慢。查询快是因为数组的空间是连续的,查询时只要通过首地址和下标很快就能找到元素。增删慢是因为数组是不能扩容的,一旦增加或者删除元素,内部操作就是新开辟一个数组把元素copy到新的数组,老的数组等待被垃圾回收。 LinkedList是基于链表实现的。相比于ArrayList其特点是查询慢,增删快。 查询慢:因为链表在内存中开辟的空间不一定是连续的(基本上不可能是连续的)所以链表实现的方式是每个元素节点都会存放自己的地址,数据以及下一个节点的地址,这样把所有的元素连接起来。所以当要查询元素时只能一个一个的往下找,相比于数组的首地址加下标会慢上不少。 Map是双列集合的超类。也就是键值对形式。 |
22.HashMap 和 Hashtable 有什么区别?
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。 HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。 HashMap不能保证随着时间的推移Map中的元素次序是不变的。 |
23.如何决定使用 HashMap 还是 TreeMap?
(1)HashMap:适用于在Map中插入、删除和定位元素。 (2)Treemap:适用于按自然顺序或自定义顺序遍历键(key)。 (3)HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap. (4)HashMap 非线程安全 TreeMap 非线程安全 (5)HashMap的结果是没有排序的,而TreeMap输出的结果是排好序的。 |
24.说一下 HashMap 的实现原理?
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。 |
25.说一下 HashSet 的实现原理?
HashSet的实现原理总结如下: ①是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。 ②当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。 ③HashSet的其他操作都是基于HashMap的。 对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。 |