> 文章列表 > 面试热点1

面试热点1

面试热点1

# 集合初始化
collection集合(单列)
List集合:
特点:....
ArrayList: 创建的时候长度是0,第一次添加元素的时候创建长度为10的数组,当添加第11个元素的时候扩容原来的1.5倍,扩容方式是创建长度为15的数组,将原来的数组中的数据复制过去,然后将指针指向新数组的地址,加载因子为10(扩容时机),扩容为原来的1.5倍数,底层是数组,查询快,增删慢,非线程安全的LinkList:  底层是双向链表,查询慢,增删快,非线程安全的
Vector: 底层是数组,初始长度为10,加载因子为10,扩容是原来的2倍数,线程安全的,其实是给方法添加了synchronized方法ArrayList和LinkList区别
数据结构上....巴拉巴拉
Array和ArrayList
ArrayList是Array的升级set集合
特点:..HashSet: 初始长度为16,加载因子为0.75,苦熬容为*2,线程不安全,数据结构是哈希表,去重是hashcode和equals,底层是复用HashMap数据存储逻辑,hashset的值是放在hashmap的k上的TreeSet: java7是二叉树,java8是红黑树,线程不安全,去重和排序实现:1 TreeSet 传参Comparator比较器,重写Comparator比较器的compare方法2 泛型对象实现Comparable接口,重写compareTo方法底层是复用TreeMap数据存储逻辑,TreeSet的值是放在TreeMap的k上的红黑树:根节点必须是黑色的,叶子节点必须是黑色的,不允许两个相邻节点颜色相同节点全是黑色或者红色的从某一个节点到其子节点的叶子节点上黑色节点数量是相同的Collections.synchronizedCollect()集合
都是加表锁,保证线程安全,但是效率底下Map集合:
TreeMap:java7底层是二叉树,java8红黑树,线程不安全
HashMap:底层是哈希表,Java7数组链表,java8数组链表红黑树,加载因子为0.75,扩容为原本来2倍数,线程不安全,在java8时候,链表转红黑树必须满足链表长度大于8,且数组长度大于64,非线程安全的,允许key为nullHashTable:初始长度为11,加载因子为0.75,9的时候扩容,每次扩容原来的2倍+1,底层是哈希表,synchronized锁表保证线程安全的,可以理解HashMap就是HashTable的轻量级实现,不允许key为nullHashTable和HashMap对比:
key..
线程..
实现上..Collections.synchronizedCollect()集合
Collections.synchronizedMap()集合
都是加表锁,保证线程安全,但是效率底下高效率的并发容器:
单列集合,内部是通过分段锁和装饰着模式实现的,即synchronized和cas实现的,但是实时性比较查
CopyOnWriteArrayList
CopyOnWriteArraySet双列集合:
ConcurrentHashMap:java7数据结构(大数组不能扩容,小数组扩容,二次hash)是分段锁,每次锁定大数组的几个索引,头插java8大数组扩容,红黑树转变,大数组的节点锁实现,尾插特殊集合:
LinkHashMap:有序
LinkHashSet:有序
IdentityHashMap:底层是通过==对比进行key的对比的,只要==为True也是不存的,注意HashMap是通过hashcode后再equals判定的map的key要求:
key保证唯一即可,任何对象都可以,但是这个对象得重写hashCode方法和equals方法,确保这个对象唯一# ByteArrayOutPutStream 内存操作流
ByteArrayOutPutStream此类实现一个输出流,数据被写道一个byte数组中,操作都是在内存中进行的,缓存会随着数据的不断写入变大,
关闭比流对象无效,所以关闭后仍然可以调用,常用toByteArray(),toString()获取数据#多线程模块:
进程:计算机调度的基本单位
线程:进程的组成单位,最先任务单位并发:多个任务在cup上交替执行
并行:多个线程在多核cup上同时执行,2核同时并行的只有2条线程,但是2核心有四线程,交替执行后同时运行的只有2条线程
串行:多个任务单个线程排队执行ThreadLocal:是本地线程栈,线程变量,线程副本,跨线程是无效的,每个线程的底层都维护了一个ThreadLocalMap集合,该map的k是ThreadLocal对象,v是目标数据,ThreadLocal就是通过操作ThreadLocalMap实现对Thread线程数据维护的,使用完毕后需要remove数据,k是弱引用,value是强引用,弱引用被gc后会造成(null,value)造成内存泄漏sleep():是线程的方法,不会释放锁下面三方都是synchronized锁的设定的线程通讯方法wait():是object 的方法,会释放锁notify() 是object ,唤醒其他持有相同锁的线程,具体唤醒谁由jvm决定notifyAll(),是object ,唤醒所有持有相同锁的线程线程通讯的方法都在Object中的原因?
因为java想让任何对象都可做为锁,Object是所有类的父类,刚好能实现这要求,而且Thread类中并没有可供线程通讯的方法为啥 wait(),notify(),notifyAll()要在同步代码块中调用本身就给同步代码块设定的Api,Api强制要求的, wait()释放锁,notify()和notifyAll()唤醒其他线程竞争锁Thread的相关方法:stop()结束线程interrupt()结束线程yield()让线程从运行状态变为就绪状态,失去cpu的使用权,重新等待cpusynchronized同步方法
修饰的方法只能在同一时间被单个线程访问,同类中的其他普通方法可以被其他线程访问线程同步的方法:(1)加锁,lock.lock,lock.unlock(2)synchronized同步方法(3)原子类->cas算法cas算法:do{}while()实现的内存值,期望值,目标值内存值==期望值,进行修改,内存值设置为目标值内存值!=期望值,重新获取内存值,将内存值设置为期望值,进行其他操作,后重复上面,(这一步叫做自旋)线程的生命周期:
新建:new..
就绪:start()等待获取cpu的使用权,而run方法只是 thread 的一个普通方法调用,还是在主线程里执行,
运行:拿到cpu后运行
阻塞:等待其他线程执行,如future.get(),wait(),sleep(),join()等待其他线程执行完毕后竞争cpuyield()方法可以使得线程失去cpu,然后和其他线程一起重新竞争cpu
死亡:run执行完毕,interrupt(),stop()多线程的实现当方式:
thread,Runnable,Callable,线程池创建
java8新糖:CompletableFuture实现异步线程池的创建:四种自带的线程池,单线程线程池,缓存线程池,固定数量线程池,定时任务线程池Executors.new ThreadPoolExecutor() 七个参数线程池处理流程:
(1) 查看核心线程池是否已满,不满就创建一条线程执行任务,核心线程数量已满就查看任务队列是否已满不满就将线程存储在任务队列中任务队列已满,就查看最大线程数量,不满就创建线程执行任务,已满就按照拒绝策略执行(2) 拒绝策略:
CallerRunsPolicy():当run普通方法执行,原来的线程执行
AbortPolicy():直接抛出异常
DiscardPolicy():直接丢弃
DiscardOldestPolicy():丢弃队列中最老的任务synchronized和Lock锁的区别
lock是一个类,更规范,需要手动释放锁,并且一定要在finally 中释放锁,可重入锁,可中断锁
synchronized是jvm的关键字,不需要手动释放锁,不可中断锁,可重入锁,不公平锁可重入锁:
概念:当一个线程再次获取自己已经持有的锁时,如果获取成功就是可重入的,底层是计数器原理,获取成功一次+1
synchronized默认就是可重入的
lock.tryLock是可重入的,lock.lock是不可重入的锁的分类:
悲观锁:synchronized,Lock
乐观锁:cas
死锁:两个线程各自持有对方需要的锁,且都不释放,一直阻塞的现象称为思索
可重入锁:
可中断锁:是否能立刻停止线程synchronized锁当线程调用Thread.interrupt只是标识该线程要被停止了,并不能立即停止该线程,但是lock锁使用tryLock或者调用lockinterruptibly()后,再次执行Thread.interrupt方法就会立马停止线程
公平锁:线程是否先申请持有锁就会先获得锁,synchronized是不公平的new ReentrantLock(true)是可以维持公平的,但是不传参数是不公平的两个线程共间共享数据?
两个线程共享变量就可以实现共享数据,但是得保障数据同步,原子类,线程安全集合常用的并发工具类
CountDownLatch,计数
Semaphore,令牌synchronized、volatile、CAS 比较
synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
volatile 提供多线程共享变量可见性和禁止指令重排序优化。
CAS 是基于冲突检测的乐观锁(非阻塞)为什么Thread类的sleep()和yield ()方法是静态的?
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行wait和sleep的区别:
wait释放锁,是object的方法,在代码块中使用
sleep是Thread的方法,不会释放锁,需要捕获异常守护线程和用户线程:守护线程是服务用户线程的,当所有的线程都是守护线程jvm将退出,用户线程结束一段时间后守护线程就结束线程优先级:
虽然我们能设置线程的优先级别(1-10数字越大优先级别越高),但是线程的优先级别是概率事件,并不是绝对的##异常Throwable
Error:重大错误,jvm错误
Exception,编译时,运行时
final,finally,finalize
final: 修饰方法,变量,类,不可重写
finally: 异常....
finalize:object类的一个方法,垃圾回收时调用,可以重写这个方法实现一些结尾工作,如关闭文件等try{}catch{}finally()
try后面必须跟多个catch或者一个finallytry{}catch{}finally()遇上return
先执行finally中的,后return,若是finally中有return,则拒绝执行其他return,异常了就执行异常中return,否则就正常执行# 多态
父类引用指向子类对象,同一对象在不同时期的表现称为多态
方法重写和方法重载
方法重写:子类重写父类方法,方法名相同,参数相同,必须是越来权限修饰符越大,且private的方法不允许重写,多态的编译是体现,static修饰的方法允许重写
方法重载:方法名相同,参数不同,多态的运行时体现#abstract class抽象类和接口interface
相同点:都是用来指定规则的,都不能有构造方法
不同:接口之间可以多继承,类之间只可以单继承,多实现
java7之前只能有抽象方法
java8开始接口中允许有默认方法,静态方法,但是不能有普通方法抽象类中可以有普通方法,静态方法,但是不能有默认方法Java9 开始还允许有私有方法抽象方法不可以synchronized,也可以static,也不可以native# heap堆和stack栈
stack:8中基本类型非static修饰的都数据存在栈中,保证速度,引用类型的数据存储地址都在栈中,局部变量都存在栈中,方法弹栈,局部变量被gc,先进后出,空间小栈中的基本类型数据同一个值只会分配一次内存,int a=1;int b=1;a和b指向的是相同的地址,声明b给b赋值的时候会先查看给定的值是否已经存在,不存在才给分配内存,jvm虚拟机栈,本地方法栈,程序计数器都在栈中
heap:方法区和普通堆内存,static修饰的类的变量在类加载时候就在堆中分配内存了,new出来的都在堆中,String数据都在方法区的String常量池,空间大,是线程共享,static变量在堆中的方法区中回想类加载到执行的过程
类加载器->jvm->执行引擎->本地库接口->本地方法区jvm的组成
堆中(线程共享): 方法区(常量池),普通堆
栈:jvm虚拟机栈,方法栈,程序计数器jvm调优(其实就是改变gc时候的内存适配):
jvm调优是java性能调优的最后一战,一般情况下我们尽量从代码和架构层面上优化服务,而不是上来就修改jvm的参数调优时机:
gc频繁造成程序性能下降,正常的内存溢出程序崩溃,调优目标:
减少gc的次数,但是会消耗内存参考dump文件:
获取dump文件需要在jvm.cfg中配置启动命令或者通过命令去查看修改jvm内存参数:
E:\\developtools\\java\\jre\\lib\\amd64\\jvm.cfg类加载:
类加载器->双亲委派模型,父类不能加载子类加载器才加载, 防止java核心Api被替换怎么获取 Java 程序使用的内存?堆使用的百分比?
可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存
Runtime.freeMemory() 方法返回剩余空间的字节数,
Runtime.totalMemory()方法总内存的字节数,
Runtime.maxMemory() 返回最大内存的字节数。# String
String a1="abc": 现在栈中存的地址中查找堆中string常量池是否有"abc"的值,有将a1变量的指针指向该地址,没就创建地址分配值
String a2=new String ("aa"):创建了两个String对象,看分配几次内存呗在常量池中分配内存存储"aa",这是第一次对象的创建再new String,第二次在堆中分配内存创建对象,只是这个对象存储的是值"aa"在常量池的地址,a2对象又存储的是new对象的地址所以:a1和a2的值的地址不一样,a1是直接数据的地址,a2的内容是a1数据的地址,有点绕口,画图# 反射
class.forName("全类名"),编译时
对象.getClass,运行时
类名.class 类加时啥是反射?
在程序运行时对于任何一个类对象,可以动态的获取到其方法,属性# 内部类和静态内部类
static class name:不需要外部对象就能实例化
inner class name:需要外部对象才能实例化# 数学运算,记着就好
Math.Round(11.5)=12
Math.Round(-11.5)=-11# 数组
数组有length属性,字符串有length()方法# switch支持的类型:字符串,byte,short,int,char,枚举,不支持:long,boolean,float,double#equals和hashcodeObject的 equals是使用的==号比较的,hashcode方法时object中的一个方法,提供的算法用于配合散列集合的equals相同,则hashcode必须相同,这是在创建该对象时候的约定的,先hashcode 然后在equalsequals不同,hashcode也有可能相等hashcode相同,equals有可能相同hashcode不相同,equals绝对不同#gc垃圾回收机制
gc线程是守护线程,负责java的垃圾回收处理判断对象是否可以被回收的条件:该对象有没有被引用,没有被引用则被回收,弱引用也会被判定为垃圾回收(被引用不一定存活,弱引用会被回收)判断对象是否被引用的算法:(1)引用计数器算法:对象被引用加1,解除医用就减1,当对象的被引用为0就可以判定为垃圾,相互(即循环)引用则永不回收,会造成内存泄漏(2)可达性分析算法:查看对象和gc root是否有直接连接或者间接连接,没有的都是垃圾对象,垃圾清理算法:(1) 标记清除算法将活着的对象标记,然后清除没有标记的对象,缺点造成内存碎片化,且在清理的时候需要暂停应用,暂时已经被摒弃(2) 标记清除压缩算法将活着的对象标记,然后清除没有标记的对象,将活着的对象移动到一起,将碎片内存融合,(3) 标记复制算法将内存分为两块,每次只使用其中一块,将被使用这块中活着的对象复制到另一块中,清理原来这块,对象数据大复制降低型性能,同时浪费内存(4) 分代清理,目前就是这样处理的,针对不同的数据类型做不同的处理新生代:新生代都是一些不可变的数据类型,String,基本类型等,数据较小,采用标记复制算法,老年代:老年代都是一些存活时间长的对象,采用的是标记清除压缩算法(大多都是清理堆中的垃圾)你能保证gc执行吗?虽然我们可以system.gc,但是gc不一定能保证执行Serial 与 Parallel收集器之间的不同之处?
同: 都会暂停程序
异: Serial 单线程的,Parallel 多线程的# 内存泄漏和内存溢出
内存泄漏:占用内存不释放,如io开启
内存溢出: 内存不够用了,申请不到内存,内存泄漏造成内存溢出# Unicode  编码
java 采用unicode码进行编码,就是每个字符对应一个数字,一个unicode码占用两个字节,8位,一个字符占用两个字节,一个unicode即表示一个字符,一个char即一个字符(含汉字)## &&和&
& 是位运算符,不能短路
&& 是逻辑运算符,会短路# xml规范dtd,schema# 静态变量和实例变量的区别
静态变量:静态变量是类变量,static修饰,属于类,类加载即分配内存,一个类不管创建多少个对象,类的静态变量在内存中只有一份,静态变量可以实现让多个对象共享一个内存,调用是通过类名.变量名/方法
实例变量:实例变量属于实例对象,必须依赖于某一实例,需要先创建对象才能访问到,调用时候必须是对象.变量/方法#jvm的组成堆中(线程共享): 方法区(常量池),普通堆
栈:jvm虚拟机栈,方法栈,程序计数器# Java序列化:
其实就是将对象通过二进制编码写出到文件保存,使用时候读入的过程
对象需要实现Serializable接口,设置常量uid应用场景:
对象需要通过流传递# java对象的克隆
深克隆:java的序列化实现cloneable接口,重写clone方法,递归实现
浅克隆:实现cloneable接口,重写clone方法,递归实现# String和包装类都是太监类
String 不可变
StringBuilder线程不安全,可变
StringBuffer线程安全,可变StringBuilder和StringBuffer都是继承AbstractStringBuilder#自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;包装类存在的必要
类型泛化
字符串转换基本类型
允许为null的情况(数据库查询映射操作)# byte表示的范围是-128到127之间,将int类型的128强转给byte,数值会变为-128,超出部分会重新在值阈循环# ==和equals的区别
对于基本类型,==比较的是值;
对于引用类型,==比较的是地址;
equals不能用于基本类型的比较;如果没有重写equals,equals就相当于==,因为object的equals是==比较;如果重写了equals方法,equals比较的是对象的内容;# BIO、NIO、AIO 有什么区别
(1)同步阻塞BIO
(2)同步非阻塞NIO,http请求就是
(3)异步非阻塞AIO,大型数据处理等# throw 和 throws 的区别?throw 在方法内抛出异常throws 在方法上声明可能有异常#java 中都有哪些引用类型?强引用 只要强引用存在,垃圾回收器将永远不会回收被引用的对象。软引用(SoftReference)在内存足够的时候,软引用不会被回收,只有在内存不足时,才会回收弱引用(WeakReference)进行垃圾回收时,弱引用就会被回收虚引用(PhantomReference)# 实例化对象的方式:new和反射机制创建,序列化# list和数组之间的转换
Arrays.as(arr)
list.toArray(new Integer[list.size()])# 如何保证集合不被修改Collections包下的unmodifiableMap可以保证集合不被修改#######
补充
#######面向对象的特征:
封装:将对象的属性行为结合为一个整体,尽可能的隐藏对象的内部细节
继承:子类继承父类的属性和行为,但是子类可以在此基础上进行扩充,提高代码的复用性
多态:封装和继承就是为多态准备的,使得同一个对象在不同的时刻有着不同的形态表现Integer.MAX_VALUE
Integer.MIN_VALUE函数式
有且只有一个抽象方法的接口叫函数式接口,函数式接口可以转换为ambdaBIO,NIO,AIO
Block io同步阻塞io,我们长写的io就是bio,简单方便]
New io 同步非阻塞io,http请求等,实现了多路复用
Async io 异步非阻塞,做回调事件等hash冲突
就是计算出的hash地址值在内存中已经被占用了
解决方式:
集合  hashset/hashmap继续比较equals
其他:继续hash,构造多个hash函数,直到值不同为止
java现在版本的一个弊端问题,冲突的可能很小,只能做优化,不能根本解决

#################web###########

1 Servlet生命周期内调用的方法过程?Init()Service()doGet或者doPostdestroy2 ioc控制反转,di依赖注入
ioc 从容器中获取bean对象
di 将bean交给Spring管理的过程3 过滤器介绍:实现filter接口,重写doFilter方法4 Spring 的bean作用范围Singleton:Bean以单例的方式存在表示每次从容器中注入Bean时,都会返回一个新的实例,prototype通常翻译为原型Request:每次HTTP请求都会创建一个新的BeanSession:同一个HttpSession共享同一个Bean,不同的HttpSession使用不同的Bean5 session 和 cookie 有什么区别?(1)存储位置不同cookie在客户端浏览器;session在服务器;(2)存储容量不同cookie<=4K,一个站点最多保留20个cookie;session没有上线,出于对服务器的保护(3括并不局限于String、integer、list、map等;(3)隐私策略不同cookie对客户端是可见的,不安全;session存储在服务器上,安全;(4)跨域支持上不同cookie支持跨域;session不支持跨域;