我发现有不少程序员面试前都是准备地好好的,什么疑难杂症,未解之谜都是准备得妥妥的,张口就来。反而到了最容易的Javajava
基础的时候,各类翻车(多是以为基础的内容太简单没有花精力),原本是可以拿到更高的薪资,就由于基础没有回答好,被程序员
抓住当成借口又砍了好几K,实在是得不偿失。因此今天给你们分享一份Java基础的面试题汇总以及解析,以便你们更好地应对面试
面试,冲击更高薪资!算法
主要是为了”安全性“和”效率“的缘故,由于:数组
一、因为String类不能被继承,因此就不会没修改,这就避免了由于继承引发的安全隐患;缓存
二、String类在程序中出现的频率比较高,若是为了不安全隐患,在它每次出现时都用final来修饰,这无疑会下降程序的执行效安全
率,因此干脆直接将其设为final一提升效率;服务器
HashMap就是数组+链表的组合实现,每一个数组元素存储一个链表的头结点,本质上来讲是哈希表“拉链法”的实现。数据结构
HashMap的链表元素对应的是一个静态内部类Entry,Entry主要包含key,value,next三个元素多线程
主要有put和get方法,put的原理是,经过hash%Entry.length计算index,此时记做Entry[index]=该元素。若是index相同
就是新入的元素放置到Entry[index],原先的元素记做Entry[index].next
get就比较简单了,先遍历数组,再遍历链表元素。
null key老是放在Entry数组的第一个元素
解决hash冲突的方法:链地址法
再散列rehash的过程:肯定容量超过目前哈希表的容量,从新调整table 的容量大小,当超过容量的最大值时,取
Integer.Maxvalue
集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象自己,出于表达上的便利,咱们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。
集合接口分为:Collection和Map,list、set实现了Collection接口
ArrayList,LinkedList都实现了java.util.List接口,
Java中的队列都有哪些,其实是问queue的实现有哪些,如:ConcurrentLinkedQueue、LinkedBlockingQueue 、
ArrayBlockingQueue、LinkedList。
关于ConcurrentLinkedQueue和LinkedBlockingQueue:
Java中Class.forName和classloader均可以用来对类进行加载。
Class.forName除了将类的.class文件加载到jvm中以外,还会对类进行解释,执行类中的static块。
而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName(name,initialize,loader)带参数也可控制是否加载static块。而且只有调用了newInstance()方法采用调用构造函数,建立类的对象。
如下特性为我的比较关注的特性,并不齐全;想了解更多,请自行搜索官方文档。
Java7特性:
1.switch case可使用String,原来只能用int和char;
2.支持2进制0b开头;支持数字中间有下划线,解析时自动剔除;
3.一次抓多个异常;用|隔开;
4.try-with-resource,在try中打开资源,系统自动在使用完后关闭;
5. Map<String, List<String>> anagrams = new HashMap<>(); 对抗Google的guava.
6.集合类能够像js中的数组同样赋值和引用了。
List<String> list = ["item"];
String item = list[0];
Set<String> set = {"item"};
Map<String, Integer> map = {"key" : 1};
int value = map["key"];
7. 把字符串常量池从permgen区移到了堆区;致使String.intern()方法在1.7以前和以后表现出现不一致;
Java8特性:
1.lambda表达式;
2.新增stream,Date,Time,Base64工具类;
3.使用metaspace,元空间替代permgen区;
4.类依赖分析器:jdeps,能够以包,目录,文件夹做为输入,输出依赖关系,没有的会显示 not found
5.jjs,能够执行JavaScript代码;
数组在随机访问数据、随机增长数据、随机删除数据的执行效率上比链表的效率高,数据量越小,二者之间效率的差距越小,数据量越大差距越大。
也欢迎你们一块儿讨论面试终于到的各类奇葩问题,特意建了一个Java技术交流群:895244712,但愿有个小圈子的朋友能够加进来,不定时分享一些技术干货,但愿能带来帮助。
详细解析:https://blog.csdn.net/sinat_29581293/article/details/70214436
这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。
String最慢的缘由:
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦建立以后该对象是不可更改的,但后二者的对象是变量,是能够更改的。
2. 再来讲线程安全
在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
若是一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中不少方法能够带有synchronized关键字,因此能够保证线程是安全的,但StringBuilder的方法则没有该关键字,因此不能保证线程安全,有可能会出现一些错误的操做。因此若是要进行的操做是多线程的,那么就要使用StringBuffer,可是在单线程的状况下,仍是建议使用速度比较快的StringBuilder。
3. 总结一下
String:适用于少许的字符串操做的状况
StringBuilder:适用于单线程下在字符缓冲区进行大量操做的状况
StringBuffer:适用多线程下在字符缓冲区进行大量操做的状况
11.hashtable和hashmap的区别
1. 存储结构
HashMap | HashTable |
数组 + 链表/红黑树 | 数组 + 链表 |
HashMap的存储规则:
优先使用数组存储, 若是出现Hash冲突, 将在数组的该位置拉伸出链表进行存储(在链表的尾部进行添加), 若是链表的长度大于设定值后, 将链表转为红黑树.
HashTable的存储规则:
优先使用数组存储, 存储元素时, 先取出下标上的元素(可能为null), 而后添加到数组元素Entry对象的next属性中(在链表的头部进行添加).
出现Hash冲突时, 新元素next属性会指向冲突的元素. 若是没有Hash冲突, 则新元素的next属性就是null
描述的有点模糊, 贴出源码会清晰一点:
Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e);
2. 扩容方式
HashMap | HashTable |
oldCap * 2 | oldCap * 2 + 1 |
3. 关于null值
HashMap |
HashTable |
key, value 都可觉得 null |
key, value 均不能够为 null |
4. 线程安全
HashMap | HashTable |
线程不安全 | 线程安全 |
大神的解释:https://blog.csdn.net/qq_27093465/article/details/52268531
传送门:http://www.javashuo.com/article/p-aryreled-a.html
String类中提供了大量的操做方法,这里例举13种关于String类经常使用的方法供你们参考。参考代码以下: package cn.mc; public class StringTestMc { private String str = "helloWorld"; /** * 将字符串变成一个字符数组 */ public void tocharyArry() { char c[] = str.toCharArray(); for (int i = 0; i < c.length; i++) { System.out.println("转为数组输出:" + c[i]); } } /** * 从字符串中取出指定位置的字符 */ public void tocharAt() { char c = str.charAt(3); System.out.println("指定字符为:" + c); } /** * 将字符串变成一个byte数组 */ public void tobyte() { byte b[] = str.getBytes(); System.out.println("转换成byte数组输出为:" + new String(b)); } /** * 取得一个字符串的长度 */ public void tolength() { int l = str.length(); System.out.println("这个字符串的长度为:" + l); } /** * 查找一个指定的字符串是否存在,返回的是字符串的位置,若是不存在,则返回-1 */ public void toindexOf() { int a1 = str.indexOf("e");// 查找字符e的位置 int a2 = str.indexOf("l", 2);// 查找l的位置,从第3个开始查找 System.out.println("e的位置为:" + a1); System.out.println("l的位置为:" + a2); } /** * 去掉字符串左右空格 */ public void totrim() { String str1 = " hello "; System.out.println("去掉左右空格后输出:" + str1.trim()); } /** * 字符串的截取 */ public void tosubstring() { System.out.println("截取后的字符为:" + str.substring(0, 3));// 截取0-3个位置的内容 System.out.println("从第3个位置开始截取:" + str.substring(2));// 从第3个位置开始截取 } /** * 按照指定的字符串拆分字符,拆分的数据将以字符串数组的形式返回 */ public void tosplit() { String s[] = str.split("e");// 按hello中的e进行字符串拆分 for (int i = 0; i < s.length; i++) { System.out.println("拆分后结果为:" + s[i]); } } /** * 将字符串进行大小写转换 */ public void tochange() { System.out.println("将\"hello\"转换成大写为:" + str.toUpperCase());// 将hello转换成大写 System.out.println("将\"HELLO\"转换成大写为:" + str.toUpperCase().toLowerCase());// 将HELLO转换成小写 } /** * 判断是否以指定的字符串开头或者结尾 */ public void tostartsWithOrendWith() { if(str.startsWith("he"))//判断字符串是否以he开头 { System.out.println("字符串是以he开头"); } if(str.endsWith("lo")) { System.out.println("字符串是以lo结尾"); } } /** * 两个String类型内容比较 */ public void toequals() { String str3="world"; if(str.equals(str3)) { System.out.println("这俩个String类型的值相等"); } else System.out.println("这俩个String类型的不值相等"); } /** * 两个字符串不区分大小写进行比较 */ public void toequalslgnoreCase() { String str4="HELLO"; if(str.equalsIgnoreCase(str4)) { System.out.println("hello和HELLO忽略大小写比较值相等"); } } /** * 将一个指定获得字符串替换成其余字符串 */ public void toreplaceAll() { String str5=str.replaceAll("l", "a"); System.out.println("替换后的结果为:"+str5); } public static void main(String[] args) { StringTest obj = new StringTest(); obj.tocharyArry(); obj.tocharAt(); obj.tobyte(); obj.tolength(); obj.toindexOf(); obj.totrim(); obj.tosubstring(); obj.tosplit(); obj.tochange(); obj.tostartsWithOrendWith(); obj.toequals(); obj.toequalslgnoreCase(); obj.toreplaceAll(); } }
有这样一类对象:当内存空间还足够,则可保留在内存中;若是内存空间在gc以后仍是很是紧张,则可抛弃这些对象。不少系统的缓存功能适合这样的场景,因此jdk1.2之后
java将引用分为了强引用、软引用、弱引用、虚引用四种,引用强度一次减弱。
接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是能够有私有方法或私有变量的,
另外,实现接口的必定要实现接口里定义的全部方法,而实现抽象类能够有选择地重写须要用到的方法,通常的应用里,最顶级的是接口,而后是抽象类实现接口,最后才到具体类实现。
还有,接口能够实现多重继承,而一个类只能继承一个超类,但能够经过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的做用.
java数据类型 字节 表示范围
byte(字节型) 1 -128~127
boolean(布尔型) 1 true或false
short(短整型) 2 -32768~32767
char(字符型) 2 从字符型对应的整型数来划分,其表示范围是0~65535
int(整型) 4 -2147483648~2147483647
float(浮点型) 4 -3.4E38~3.4E38
double(双精度型) 8 -1.7E308~1.7E308
long(长整型) 8 -9223372036854775808 ~ 9223372036854775807
大神又来了:https://blog.csdn.net/qq_27093465/article/details/52279473
Hash算法解决冲突的方法通常有如下几种经常使用的解决方法
1, 开放定址法:
所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
公式为:fi(key) = (f(key)+di) MOD m (di=1,2,3,……,m-1)
※ 用开放定址法解决冲突的作法是:当冲突发生时,使用某种探测技术在散列表中造成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者
碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则代表表
中无待查的关键字,即查找失败。
好比说,咱们的关键字集合为{12,67,56,16,25,37,22,29,15,47,48,34},表长为12。 咱们用散列函数f(key) = key mod l2
当计算前S个数{12,67,56,16,25}时,都是没有冲突的散列地址,直接存入:
计算key = 37时,发现f(37) = 1,此时就与25所在的位置冲突。
因而咱们应用上面的公式f(37) = (f(37)+1) mod 12 = 2。因而将37存入下标为2的位置:
2, 再哈希法:
再哈希法又叫双哈希法,有多个不一样的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数
计算地址,直到无冲突。虽然不易发生汇集,可是增长了计算时间。
3, 链地址法:
链地址法的基本思想是:每一个哈希表节点都有一个next指针,多个哈希表节点能够用next指针构成一个单向链表,被分配到同一个索引上的多个节点能够用这个单向
链表链接起来,如:
键值对k2, v2与键值对k1, v1经过计算后的索引值都为2,这时及产生冲突,可是能够通道next指针将k2, k1所在的节点链接起来,这样就解决了哈希的冲突问题
4, 创建公共溢出区:
这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一概填入溢出表
参考地址:https://blog.csdn.net/neosmith/article/details/17068365
好了,今天的分享就到这里了,但愿可以帮助到须要面试的道友顺利渡劫。高深的问题当然要好好回答,但基础也不能落下,顾
此失彼致使薪资被砍相信也不是你们但愿看到的,反正我是经历过,很难受,哈哈。