2020Java开发工程师面试知识储备(三)java常用类(待补充),多线程
功利性得去找了下面试题库,发现一个讲面试题的教程,觉得讲得不错,教程地址
在这里总结一下学到的知识,打一下Java基础,算是挖一个新坑。
代码地址:源码地址
发现了个刷题的网站,题量大,有解析,暂未发现收费现象,社区氛围也不错,地址:网站地址
正在根据错题内容,逐渐重构完善本系列文章结构。
第四章 Java常用类解析
1. String,StringBuffer,StringBuilder
String是一个final修饰的类,所有属性也是final的,所以String具有不可变性,也就是对字符串的操作,如拼接、剪切都会产生新的String对象。
StringBuffer本质是一个线程安全(用synchronize修饰一些方法)的可修改字符串序列,因为保证线程安全,所有会带来额外的性能消耗。
StringBuilder本质上和StringBuffer没有区别,但是StringBuilder去掉了线程安全部分,提高了操作效率,是绝大部分情况下字符串拼接的首选。
如果确定拼接字符串会发生多次,并且长度可预计,那么可以在开始的时候指定合适的大小,避免数组扩容造成的开销。
1.1 String对象创建过程
例:下面代码创建了几个对象,输出是什么
String s1 = new String(“abc”);
String s2 = new String(“abc”);
System.out.println(s1 == s2);
答:String s1 = new String(“abc”);这里"abc"是静态文字池中的一个对象,new String()时,会将池中的对象复制一份到heap中,并把heap堆中的引用交给s1。
上述代码中,pool有一个对象,heap中有两个对象,s1,s2是引用,==比较的是内存地址,两个引用指向的是不同的地址,所以输出false。
2. HashMap和Hashtable
2.1 HashMap和Hashtable的区别
1)Hashtable继承自Dictionary,HashMap继承自AbstractMap,二者都实现了Map接口。
2)Hashtable不允许null key和null value,HashMap允许。
3)HashMap的使用几乎和Hashtable相同,不过H俺是table是synchronize同步的。
2.2 HashMap的put方法
java1.7版本:数据结构是链表+数组。在新元素插入,触发Map扩容的时候,会涉及到计算新元素在新Map中的位置,需要用到头插法(线程不安全)。多线程下可能会造成环形引用,进入死循环。
java1.8版本:数据结构是链表+数组+红黑树。
当一个链表的长度大于8的时候,put会进行红黑树的操作(如果总容量小于64先扩容)
当红黑树中元素个数小于6的时候,再从红黑树转回链表。
多线程进行put操作的时候,可能会出现数据被覆盖的情况。
3. ArrayList和LinkList
1)ArrayList底层是动态数组,LinkList底层是双链表
2)ArrayList添加元素和删除元素涉及到数组元素的移动,向指定位置添加/删除元素效率没有LinkList高
3)对于随机访问get和set,ArrayList优于LinkList
4. &和&&
都是与运算,a与b 全真得真,有假得假
&没有短路前后两个表达式都运行
&&有短路,第一个表达式false的时候就不运行第二个表达式了
5. final修饰符
final修饰值类型,变量一旦赋值就不能改变。
final修饰引用类型,被引用的对象属性值可以改变,但是该引用不能指向新的对象(不能new)。
6. ==和equals()
==:如果比较的对象是基本数据类型,则比较的是数值是否一致;如果比较的是引用数据类型,则比较的是对象的地址值是否一致。
equals():equals()方法不能用于比较基本数据类型的对象,如果对象和自身进行比较,则equals()方法与==是一样的(比较引用地址)。对于String类 Date类 File类等 可重写equals() 方法用于比较对象的属性内容是否一致(比较重写的逻辑指定的值)。
当Integer和int相比时,Integer会被拆箱,比较数值,Integer i3 =Integer.valueOf(59);
前面有个Integer i1 = 59;
所以i3引用i1。
7. include
静态的include:是jsp的指令来实现的,<% @ include file=“xx.html”%> 特点是 共享request请求域,先包含再编译,不检查包含页面的变化。不接受重名冲突。
动态的include:是jsp动作来实现的,<jsp:include page=“xx.jsp” flush=“true”/> 这个是不共享request请求域,先编译在包含,是要检查包含页面的变化的。可以重名。
8. Date
Date date=new Date();
System.out.printf("%tD%n",date);
tD 美国格式的日期 07/31/20月日年(2020/07/31)
tT 24小时时间 13:20:18 当前时间(时分秒)
tY 四位的年 2020 年份
助记:D(day月日年)T(time时分秒)Y(year年)
第五章 多线程
1. 实现方式
1.1 继承Thread类
首先新建一个类,继承Thread类,重写run方法,需要多线程执行该类的run方法时,new多个该类的实例,然后start方法。
1.2 实现Runnable接口
新建一个类,重写run方法(避免单继承的局限,适合多个线程处理同一资源的情况)
需要多线程执行时先实例化刚新建的类,然后把实例作为参数新建Thread对象,并startnew Thread(runnable).start();
用同一个实例新建的Thread资源是共享的。
1.3 实现Callable接口
重写call方法,允许返回值抛出异常
新建部分和Runnable类似,只是在call方法中多了个返回值。调用部分:初始化一个实例:Callable<Integer> callable = new MyCallable();
,用该实例新建FutureTask对象实例:FutureTask<Integer> task1 = new FutureTask<>(callable);
用FutureTask实例创建线程实例并运行。new Thread(task1).start();
返回值从FutureTask实例的get方法获取Integer num1 = task1.get();
1.4 使用线程池
减少创建新线程的时间,重复利用线程池中线程,降低资源消耗,可有返回值
关注线程的获取,可以将上面的线程类放到线程池里进一步限制线程的实现方式。
Executors.newFixedThreadPool方法,新建ExecutorService实例(线程池)
新建runnable,callable实例。用作线程池的submit方法的参数,可以直接运行这两个实例的多线程。
runnable 直接submit。
callable 也是直接submit,返回值是Future,可以用get方法获取callable的返回值。
2. 同步问题
2.1 synchronized和ReentrantLock
1)synchronized:可以用来同步方法和同步代码块。
同步方法:给一个方法增加synchronize关键字,可以是静态方法(锁住整个类)也可以是非静态方法(不能是抽象方法)
同步代码块:通过锁定一个指定的对象,来对同步代码块进行同步。
同步是高开销的操作,尽量少用同步方法,同步关键代码的代码块即可。
2)ReentrantLock:可重入锁,代码通过lock()方法获取锁,但必须调用unlock()方法释放锁。
注:当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程对该对象的该synchronized方法或者synchronized代码块的访问将被阻塞。
当在对象上调用其任意synchronized方法的时候,对象都被加锁。
当一个线程访问某对象的synchronized方法或者synchronized代码块时,其他线程仍然可以访问该对象的非同步代码块。
2.2 sleep()和wait()
①sleep是线程类(Thread)的方法,wait是Object类的方法;
②sleep不释放对象锁,wait放弃对象锁
③sleep暂停线程、但监控状态仍然保持,结束后会自动恢复
④wait后进入等待锁定池,只有针对此对象发出notify或者notifyall方法(这里经常会漏一个)后获得对象锁进入就绪状态(这里经常会挖坑“运行状态”)
⑤两个方法都需要InterruptedException异常处理。
⑥wait()方法可以有参数,也可以无参数;sleep()方法必须要传入long的参数。
本文地址:https://blog.csdn.net/qq_41109942/article/details/107656716