面试

1.获取string每个字符出现的次数: 把每个字符遍历出来,存到map里面,用字符做key,次数做value,每次便利用map获取,有的话value+1,没有就put。

2.单例模式,饿汉式、懒汉式。1 在类中使用构造器创建静态常量,随着类加载而加载出来,在静态方法中返回,线程安全 ,也可以是用枚举2使用静态内部类,在静态内部类中使用懒汉式方法创建

3.集合

Connection接口
	----list有序可重复
	----set无序不可重复
Map接口

list 都可以放null

arraylist 适合查询,线程不安全,父接口有个synchronizedlist方法,把集合传进去会返回一个线程安全的集合
1.7 创建集合就给10的长度 扩容1.5倍
1.8 创建集合给一个空数组 在添加数据时在创建长度为10的数组,扩容 1.5倍

linkedlist 适合增删改 线程不安全
底层双向链表方式,添加的时候会把last地址放在要添加的这个元素的头,尾放null

vetor 线程安全
1.7和1.8都创建的时候创建长度为10的数组

set
hashset 数组加链表 适合增 注意安全remove也是根据添加的方式先计算hash散列算法一系列操作去找 可以放null
1.7创建集合就创建16长度的数组
1.8 添加的时候在创建 添加方式根据要添加的对象的hash值通过散列算法确定在数组中的位置,位置没有就添加,如果位置有就比较hash值,如果不等就以链表方式存储,七上八下规则,如果等,就比较equals,用要添加的元素去equals存在的元素,如果等就存,如果不等就链表存,同样七上八下

linkedhashset可以按照添加顺序遍历数据 适合多遍历 线程不安全
在添加对象的时候加了双向链表,头指向前一个地址,尾指向后一个地址,这个前一个后一个是按照添加顺序决定的

treeset 可以根据对象指定属性排序 底层是红黑树
添加的对象类型一致,比较方法可以在类中实现compareTo接口,在实现方法中比较。特点是有序 查询比list快

Map接口
hashmap可以存储null,线程不安全,效率高
1.7数组加链表 在创建集合对象的时候就已经创建了长度16的数组,在put方法,根据要存储的Entry对象的key值计算出hash值,hash值是key的hashcode值经过高位运算,然后根据hash值与数组长度-1的或操作(相当于去模)得到了这个key在数组中的位置,如果这个位置没有元素就直接存储上去
(此时要存储的位置假如为空,就直接存储,而不会扩容,因为1.7中扩容需要超过临界值且要存储的位置不为空,在进行扩容);假如此时位置上有值,就会把这个位置 上所有的Entry对象的key来和要存储的这个Entry的key来比较(先比较hash值,在比较key的地址值,在比较equals值),如果相同就会覆盖对象的value值,如果不相同就会添加,再添加的时候会考虑扩容问题,这个要看size(此时已经存了几个了)是否大于等于临界值,大于就扩容2倍,把原有的值重新弄到新的数组上,并且重新计算此entry的hash值,在新的数组上添加;没有超过就不需要扩容,然后在进行链表存储,要存储的值得next指向原有的值地址值;七上八下。

1.8数组加链表加红黑树
创建集合创建数组为空,不会指定长度,先给加载因子赋值为0.75,然后在put的时候判断此时数组是否为空,为空就进行扩容,创建长度为16的数组,临界值为长度乘以加载因子,然后在进行增加值,根据要存储的Node对象的key值计算出hash值,hash值是key的hashcode值经过高位运算,然后根据hash值与数组长度-1的或操作(相当于去模)得到了这个key在数组中的位置,如果此时位置为空就进行添加操作,再添加后再判断size是否大于临界值,大于就扩容,不大于就结束;如果位置上有值,就先比较两个的key(先比较hash值,在比较key的地址值,在比较equals值),如果key相等就覆盖value;如果两个key不一样,判断是否为treeNode,假如是 直接红黑树插入键值对,再判断size是否大于临界值,大于就扩容,不大于就结束;如果不是treeNode,就把这个位置上的链表的key遍历(拿e=p.next)出来和这个key比较,如果都不相同,则最后一个p.next==null,则存储p.next = newNode(hash, key, value, null),然后就判断这个链表的长度是否大于8,如果大于就进行判断此时数组长度是否大于64,如果小于64,就进行扩容;大于64就把链表转换成红黑树,最后在判断size是否大于临界值,大于就扩容,不大于就结束。

hashmap子类linkedhashmap: 在原有的hashmap底层结构添加双向链表指向前后对象的地址,可以按照添加顺序遍历出来,适合遍历次数多的集合
treemap:根据key-value对排序,考虑定制排序和自然排序。底层红黑树
hashtable不能存储null的key和value 线程安全 效率低 底层是数组 链表
hashtable子类properties

4、
tcp和udp
tcp相对于udp可以进行大量数据传输,速度相对于较慢,数据不会泄露,会建立连接并且释放资源,更可靠。
udp:将数据,源、目的封装成数据包,数据包大小限制在64k以内,不可靠,速度较快,数据可能会泄露,不会建立连接也不会释放连接。
三次握手、四次放手
SYN
seq
ACK
ack
创建多线程的方式有几种?
1、继承 Thread 类创建线程类
2、通过 Runnable 接口创建线程类
3、通过 Callable 和 Future 创建线程,通过实现 Callback 接口,并用 Future 可以来接收多线程的执行结果。
4、通过线程池创建
JDK1.5 开始,Java API 提供了 Executor 框架让你可以创建不同的线程池。
在java中wait和sleep方法的不同
Java线程具有五中基本状态
1、新建状态(New):
2、就绪状态(Runnable):
3、运行状态(Running):
4、阻塞状态(Blocked):
5、死亡状态(Dead):
Java线程池中submit() 和 execute()方法有什么区别?
两个方法都可以向线程池提交任务,execute()方法的返回类型是 void,它定义在 Executor 接口中。而 submit()方法可以返回持有计算结果的 Future 对象,它定义在 ExecutorService 接口中,它扩展了 Executor 接口,其它线程池类像 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都有这些方法。
volatile
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。(实现可见性)
禁止进行指令重排序。(实现有序性)
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。
jmm java内存模型
8个原子操作 读取,加载,使用,赋值,存储,写入,锁定,解锁
当给变量用volatile关键字修饰,有个以lock开始的汇编指令,这个指令会开始总线MESI缓存一致协议和cpu嗅探机制和当线程中本地内存被赋值以后需要存储和写入到总内存这几步操作的加锁(因为当发生了赋值操作修改总内存值,会触发其他线程的嗅探机制,这时候会把其他线程工作内存中的这一份值失效,当在线程中使用的时候,又会去总内存读,避免在写入之前读,所以会加锁),并且在开启了以后,只要有赋值操作,不管后面还有多少业务代码,都会以最快速度走写入总内存这步操作
java对象布局?
对象头(96bit)、实例变量、填充数据因为三部分加起来必须是8bit的倍数,所以填充数据有时候可以没有
对象头由 make word (64bit)和 Class MetaData Address/Klass pointer(32bit/64bit(指针压缩))两个词 组成,对象头信息包括堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息。

你可能感兴趣的:(笔记)