ThreadLocal(线程变量副本)
Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量。
采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
ThreadLocal在Spring中发挥着巨大的作用,在管理Request作用域中的Bean、事务管理、任务调度、AOP等模块都出现了它的身影。
Spring中绝大部分Bean都可以声明成Singleton作用域,采用ThreadLocal进行封装,因此有状态的Bean就能够以singleton的方式在多线程中正常工作了。
友情链接:深入研究java.lang.ThreadLocal类
Java内存模型:
Java虚拟机规范中将Java运行时数据分为六种。
1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。
2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。
3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
4.Java堆:所有线程共享的一块内存区域,对象实例几乎都在这分配内存。
5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
6.运行时常量池:代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。
友情链接: Java中JVM虚拟机详解
“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”
在什么时候:
1.新生代有一个Eden区和两个survivor区,首先将对象放入Eden区,如果空间不足就向其中的一个survivor区上放,如果仍然放不下就会引发一次发生在新生代的minor GC,将存活的对象放入另一个survivor区中,然后清空Eden和之前的那个survivor区的内存。在某次GC过程中,如果发现仍然又放不下的对象,就将这些对象放入老年代内存里去。
2.大对象以及长期存活的对象直接进入老年区。
3.当每次执行minor GC的时候应该对要晋升到老年代的对象进行分析,如果这些马上要到老年区的老年对象的大小超过了老年区的剩余大小,那么执行一次Full GC以尽可能地获得老年区的空间。
对什么东西:从GC Roots搜索不到,而且经过一次标记清理之后仍没有复活的对象。
做什么: 新生代:复制清理; 老年代:标记-清除和标记-压缩算法; 永久代:存放Java中的类和加载类的类加载器本身。
GC Roots都有哪些: 1. 虚拟机栈中的引用的对象 2. 方法区中静态属性引用的对象,常量引用的对象 3. 本地方法栈中JNI(即一般说的Native方法)引用的对象。
友情链接:Java GC的那些事(上)
友情链接:Java GC的那些事(下)
友情链接:CMS垃圾收集器介绍
Synchronized 与Lock都是可重入锁,同一个线程再次进入同步代码的时候.可以使用自己已经获取到的锁。
Synchronized是悲观锁机制,独占锁。而Locks.ReentrantLock是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。 ReentrantLock适用场景
- 某个线程在等待一个锁的控制权的这段时间需要中断
- 需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程,锁可以绑定多个条件。
- 具有公平锁功能,每个到来的线程都将排队等候。
友情链接: Synchronized关键字、Lock,并解释它们之间的区别
StringBuffer是线程安全的,每次操作字符串,String会生成一个新的对象,而StringBuffer不会;StringBuilder是非线程安全的
友情链接:String、StringBuffer与StringBuilder之间区别
fail-fast:机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件
happens-before:如果两个操作之间具有happens-before 关系,那么前一个操作的结果就会对后面一个操作可见。
1.程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
2.监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
3.volatile变量规则:对一个volatile域的写,happens- before于任意后续对这个volatile域的读。
4.传递性:如果A happens- before B,且B happens- before C,那么A happens- before C。
5.线程启动规则:Thread对象的start()方法happens- before于此线程的每一个动作。
Volatile和Synchronized四个不同点:
1 粒度不同,前者针对变量 ,后者锁对象和类
2 syn阻塞,volatile线程不阻塞
3 syn保证三大特性,volatile不保证原子性
4 syn编译器优化,volatile不优化 volatile具备两种特性:
1.保证此变量对所有线程的可见性,指一条线程修改了这个变量的值,新值对于其他线程来说是可见的,但并不是多线程安全的。
2.禁止指令重排序优化。
Volatile如何保证内存可见性:
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。
2.当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。
同步:就是一个任务的完成需要依赖另外一个任务,只有等待被依赖的任务完成后,依赖任务才能完成。
异步:不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,只要自己任务完成了就算完成了,被依赖的任务是否完成会通知回来。(异步的特点就是通知)。 打电话和发短信来比喻同步和异步操作。
阻塞:CPU停下来等一个慢的操作完成以后,才会接着完成其他的工作。
非阻塞:非阻塞就是在这个慢的执行时,CPU去做其他工作,等这个慢的完成后,CPU才会接着完成后续的操作。
非阻塞会造成线程切换增加,增加CPU的使用时间能不能补偿系统的切换成本需要考虑。
友情链接:Java并发编程之volatile关键字解析
CAS(Compare And Swap) 无锁算法: CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
友情链接:非阻塞同步算法与CAS(Compare and Swap)无锁算法
线程池的作用: 在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。
常用线程池:ExecutorService 是主要的实现类,其中常用的有 Executors.newSingleThreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。
友情链接:线程池原理
友情链接:线程池原理解析
类加载器工作机制:
1.装载:将Java二进制代码导入jvm中,生成Class文件。
2.连接:a)校验:检查载入Class文件数据的正确性 b)准备:给类的静态变量分配存储空间 c)解析:将符号引用转成直接引用
3:初始化:对类的静态变量,静态方法和静态代码块执行初始化工作。
双亲委派模型:类加载器收到类加载请求,首先将请求委派给父类加载器完成 用户自定义加载器->应用程序加载器->扩展类加载器->启动类加载器。
友情链接:深入理解Java虚拟机笔记—双亲委派模型
友情链接:JVM类加载的那些事
友情链接:JVM(1):Java 类的加载机制
一致性哈希:
Memcahed缓存:
数据结构:key,value对
使用方法:get,put等方法
友情链接:hashcode(),equal()方法深入解析
Redis数据结构: String—字符串(key-value 类型)
Hash—字典(hashmap) Redis的哈希结构可以使你像在数据库中更新一个属性一样只修改某一项属性值
List—列表 实现消息队列
Set—集合 利用唯一性
Sorted Set—有序集合 可以进行排序 可以实现数据持久化
友情链接: Spring + Redis 实现数据的缓存
java自动装箱拆箱深入剖析
谈谈Java反射机制
如何写一个不可变类?
索引:B+,B-,全文索引
Mysql的索引是一个数据结构,旨在使数据库高效的查找数据。
常用的数据结构是B+Tree,每个叶子节点不但存放了索引键的相关信息还增加了指向相邻叶子节点的指针,这样就形成了带有顺序访问指针的B+Tree,做这个优化的目的是提高不同区间访问的性能。
什么时候使用索引:
- 经常出现在group by,order by和distinc关键字后面的字段
- 经常与其他表进行连接的表,在连接字段上应该建立索引
- 经常出现在Where子句中的字段
- 经常出现用作查询选择的字段
友情链接:MySQL:InnoDB存储引擎的B+树索引算法
友情链接:MySQL索引背后的数据结构及算法原理
Spring IOC (控制反转,依赖注入)
Spring支持三种依赖注入方式,分别是属性(Setter方法)注入,构造注入和接口注入。
在Spring中,那些组成应用的主体及由Spring IOC容器所管理的对象被称之为Bean。
Spring的IOC容器通过反射的机制实例化Bean并建立Bean之间的依赖关系。
简单地讲,Bean就是由Spring IOC容器初始化、装配及被管理的对象。
获取Bean对象的过程,首先通过Resource加载配置文件并启动IOC容器,然后通过getBean方法获取bean对象,就可以调用他的方法。
Spring Bean的作用域:
Singleton:Spring IOC容器中只有一个共享的Bean实例,一般都是Singleton作用域。
Prototype:每一个请求,会产生一个新的Bean实例。
Request:每一次http请求会产生一个新的Bean实例。
友情链接: Spring框架IOC容器和AOP解析
友情链接:浅谈Spring框架注解的用法分析
友情链接:关于Spring的69个面试问答——终极列表
代理的共有优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。
Java静态代理:
代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,代理对象可以在调用目标对象相应方法前后加上其他业务处理逻辑。
缺点:一个代理类只能代理一个业务类。如果业务类增加方法时,相应的代理类也要增加方法。
Java动态代理:
Java动态代理是写一个类实现InvocationHandler接口,重写Invoke方法,在Invoke方法可以进行增强处理的逻辑的编写,这个公共代理类在运行的时候才能明确自己要代理的对象,同时可以实现该被代理类的方法的实现,然后在实现类方法的时候可以进行增强处理。
实际上:代理对象的方法 = 增强处理 + 被代理对象的方法
JDK和CGLIB生成动态代理类的区别:
JDK动态代理只能针对实现了接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑
CGLIB是针对类实现代理,主要是对指定的类生成一个子类(没有实例化一个类),覆盖其中的方法 。
Spring AOP应用场景
性能检测,访问控制,日志管理,事务等。
默认的策略是如果目标类实现接口,则使用JDK动态代理技术,如果目标对象没有实现接口,则默认会采用CGLIB代理
SpringMVC运行原理
- 客户端请求提交到DispatcherServlet
- 由DispatcherServlet控制器查询HandlerMapping,找到并分发到指定的Controller中。
- Controller调用业务逻辑处理后,返回ModelAndView
- DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
- 视图负责将结果显示到客户端
友情链接:Spring:基于注解的Spring MVC(上)
友情链接: Spring:基于注解的Spring MVC(下)
友情链接:SpringMVC与Struts2区别与比较总结
友情链接:SpringMVC与Struts2的对比
一个Http请求
DNS域名解析 –> 发起TCP的三次握手 –> 建立TCP连接后发起http请求 –> 服务器响应http请求,浏览器得到html代码 –> 浏览器解析html代码,并请求html代码中的资源(如javascript、css、图片等) –> 浏览器对页面进行渲染呈现给用户
设计存储海量数据的存储系统:设计一个叫“中间层”的一个逻辑层,在这个层,将数据库的海量数据抓出来,做成缓存,运行在服务器的内存中,同理,当有新的数据到来,也先做成缓存,再想办法,持久化到数据库中,这是一个简单的思路。主要的步骤是负载均衡,将不同用户的请求分发到不同的处理节点上,然后先存入缓存,定时向主数据库更新数据。读写的过程采用类似乐观锁的机制,可以一直读(在写数据的时候也可以),但是每次读的时候会有个版本的标记,如果本次读的版本低于缓存的版本,会重新读数据,这样的情况并不多,可以忍受。
友情链接: HTTP与HTTPS的区别
友情链接: HTTPS 为什么更安全,先看这些
友情链接: HTTP请求报文和HTTP响应报文
友情链接: HTTP 请求方式: GET和POST的比较
Session与Cookie:Cookie可以让服务端跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果Cookie很多,则无形的增加了客户端与服务端的数据传输量,
而Session则很好地解决了这个问题,同一个客户端每次和服务端交互时,将数据存储通过Session到服务端,不需要每次都传回所有的Cookie值,而是传回一个ID,每个客户端第一次访问服务器生成的唯一的ID,客户端只要传回这个ID就行了,这个ID通常为NAME为JSESSIONID的一个Cookie。这样服务端就可以通过这个ID,来将存储到服务端的KV值取出了。
Session和Cookie的超时问题,Cookie的安全问题
分布式Session框架
- 配置服务器,Zookeeper集群管理服务器可以统一管理所有服务器的配置文件
- 共享这些Session存储在一个分布式缓存中,可以随时写入和读取,而且性能要很好,如Memcache,Tair。
- 封装一个类继承自HttpSession,将Session存入到这个类中然后再存入分布式缓存中
- 由于Cookie不能跨域访问,要实现Session同步,要同步SessionID写到不同域名下。
适配器模式:将一个接口适配到另一个接口,Java I/O中InputStreamReader将Reader类适配到InputStream,从而实现了字节流到字符流的准换。
装饰者模式:保持原来的接口,增强原来有的功能。
FileInputStream 实现了InputStream的所有接口,BufferedInputStreams继承自FileInputStream是具体的装饰器实现者,将InputStream读取的内容保存在内存中,而提高读取的性能。
Spring事务配置方法:
1.切点信息,用于定位实施事物切面的业务类方法
2.控制事务行为的事务属性,这些属性包括事物隔离级别,事务传播行为,超时时间,回滚规则。
Spring通过aop/tx Schema 命名空间和@Transaction注解技术来进行声明式事物配置。
Mybatis
每一个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心。首先用字节流通过Resource将配置文件读入,然后通过SqlSessionFactoryBuilder().build方法创建SqlSessionFactory,然后再通过SqlSessionFactory.openSession()方法创建一个SqlSession为每一个数据库事务服务。
经历了Mybatis初始化 –>创建SqlSession –>运行SQL语句,返回结果三个过程
Servlet和Filter的区别:
整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter有如下几个用处:
Filter可以进行对特定的url请求和相应做预处理和后处理。
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest。
根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse。
根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
实际上Filter和Servlet极其相似,区别只是Filter不能直接对用户生成响应。实际上Filter里doFilter()方法里的代码就是从多个Servlet的service()方法里抽取的通用代码,通过使用Filter可以实现更好的复用。
Filter和Servlet的生命周期:
1.Filter在web服务器启动时初始化
2.如果某个Servlet配置了 1 ,该Servlet也是在Tomcat(Servlet容器)启动时初始化。
3.如果Servlet没有配置1 ,该Servlet不会在Tomcat启动时初始化,而是在请求到来时初始化。
4.每次请求, Request都会被初始化,响应请求后,请求被销毁。
5.Servlet初始化后,将不会随着请求的结束而注销。
6.关闭Tomcat时,Servlet、Filter依次被注销。
HashMap与HashTable的区别。
1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
HashMap的实现机制:
- 维护一个每个元素是一个链表的数组,而且链表中的每个节点是一个Entry[]键值对的数据结构。
- 实现了数组+链表的特性,查找快,插入删除也快。
- 对于每个key,他对应的数组索引下标是 int i = hash(key.hashcode)&(len-1);
- 每个新加入的节点放在链表首,然后该新加入的节点指向原链表首
HashMap和TreeMap区别
友情链接: Java中HashMap和TreeMap的区别深入理解
HashMap冲突
友情链接: HashMap冲突的解决方法以及原理分析
友情链接: HashMap的工作原理
友情链接: HashMap和Hashtable的区别
友情链接: 2种办法让HashMap线程安全
HashMap,ConcurrentHashMap与LinkedHashMap的区别
- ConcurrentHashMap是使用了锁分段技术技术来保证线程安全的,锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问
- ConcurrentHashMap 是在每个段(segment)中线程安全的
- LinkedHashMap维护一个双链表,可以将里面的数据按写入的顺序读出
ConcurrentHashMap应用场景
1:ConcurrentHashMap的应用场景是高并发,但是并不能保证线程安全,而同步的HashMap和HashMap的是锁住整个容器,而加锁之后ConcurrentHashMap不需要锁住整个容器,只需要锁住对应的Segment就好了,所以可以保证高并发同步访问,提升了效率。
2:可以多线程写。
ConcurrentHashMap把HashMap分成若干个Segmenet
1.get时,不加锁,先定位到segment然后在找到头结点进行读取操作。而value是volatile变量,所以可以保证在竞争条件时保证读取最新的值,如果读到的value是null,则可能正在修改,那么就调用ReadValueUnderLock函数,加锁保证读到的数据是正确的。
2.Put时会加锁,一律添加到hash链的头部。
3.Remove时也会加锁,由于next是final类型不可改变,所以必须把删除的节点之前的节点都复制一遍。
4.ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对Hash表的不同Segment进行的修改。
ConcurrentHashMap的应用场景是高并发,但是并不能保证线程安全,而同步的HashMap和HashTable的是锁住整个容器,而加锁之后ConcurrentHashMap不需要锁住整个容器,只需要锁住对应的segment就好了,所以可以保证高并发同步访问,提升了效率。
ConcurrentHashMap能够保证每一次调用都是原子操作,但是并不保证多次调用之间也是原子操作。
友情链接:Java集合—ConcurrentHashMap原理分析
Vector和ArrayList的区别
友情链接:Java中Vector和ArrayList的区别
ExecutorService service = Executors…. ExecutorService service = new ThreadPoolExecutor() ExecutorService service = new ScheduledThreadPoolExecutor();
ThreadPoolExecutor源码分析
线程池本身的状态:
等待任务队列和工作集:
线程池的主要状态锁:
线程池的存活时间和大小:
1.2 ThreadPoolExecutor 的内部工作原理
有了以上定义好的数据,下面来看看内部是如何实现的 。 Doug Lea 的整个思路总结起来就是 5 句话:
- 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
- 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
- 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
- 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
- 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。
Executor包结构
CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。
使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。
Linux常用命令:cd,cp,mv,rm,ps(进程),tar,cat(查看内容),chmod,vim,find,ls
死锁的必要条件
- 互斥 至少有一个资源处于非共享状态
- 占有并等待
- 非抢占
- 循环等待
解决死锁,第一个是死锁预防,就是不让上面的四个条件同时成立。二是,合理分配资源。
三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。
进程间的通信方式
- 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
- 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
进程与线程的区别和联系
操作系统的进程调度算法
计算机系统的层次存储结构详解
数据库事务是指作为单个逻辑工作单元执行的一系列操作。
友情链接:数据库事务的四大特性以及事务的隔离级别
MySQL数据库优化总结
MYSQL 优化常用方法
MySQL存储引擎--MyISAM与InnoDB区别
关于SQL数据库中的范式
Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,当程序调用save(),update(),saveOrUpdate()等方法 及调用查询接口list,filter,iterate时,如Session缓存中还不存在相应的对象,Hibernate会把该对象加入到一级缓存中,当Session关闭的时候缓存也会消失。
Hibernate的一级缓存是Session所内置的,不能被卸载,也不能进行任何配置一级缓存采用的是key-value的Map方式来实现的,在缓存实体对象时,对象的主关键字ID是Map的key,实体对象就是对应的值。
Hibernate二级缓存:把获得的所有数据对象根据ID放入到第二级缓存中。Hibernate二级缓存策略,是针对于ID查询的缓存策略,删除、更新、增加数据的时候,同时更新缓存。
进程和线程的区别:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有三种手段,一种是继续Thread类,另外一种是实现Runable接口,还有就是实现Callable接口。
Switch能否用string做参数?
a.在 Java 7 之前, switch 只能支持byte,short,char,int 或者其对应的封装类以及 Enum 类型。在Java 7中,String 支持被加上了。
Object有哪些公用方法?
a.方法equals测试的是两个对象是否相等
b.方法clone进行对象拷贝
c.方法getClass返回和当前对象相关的Class对象
d.方法notify,notifyall,wait都是用来对给定对象进行线程同步的
Java的四种引用,强弱软虚,以及用到的场景
a.利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题。
b.通过软可及对象重获方法实现Java对象的高速缓存:比如我们创建了一Employee的类,如果每次需要查询一个雇员的信息。哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这是需要消耗很多时间的。我们可以通过软引用和 HashMap 的结合,先是保存引用方面:以软引用的方式对一个Employee对象的实例进行引用并保存该引用到HashMap 上,key 为此雇员的 id,value为这个对象的软引用,另一方面是取出引用,缓存中是否有该Employee实例的软引用,如果有,从软引用中取得。如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,并保存对这个新建实例的软引用。
c.强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
d.软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
e.弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。
f.虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
Hashcode的作用,与 equal 有什么区别?
a.同样用于鉴定2个对象是否相等的,java集合中有 list 和 set 两类,其中 set不允许元素重复实现,那个这个不允许重复实现的方法,如果用 equal 去比较的话,如果存在1000个元素,你 new 一个新的元素出来,需要去调用1000次 equal 去逐个和他们比较是否是同一个对象,这样会大大降低效率。hashcode实际上是返回对象的存储地址,如果这个位置上没有元素,就把元素直接存储在上面,如果这个位置上已经存在元素,这个时候才去调用equal方法与新元素进行比较,相同的话就不存了,散列到其他地址上。
Override和Overload的含义以及区别
a.Overload顾名思义是重新加载,它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;或者说可以改变参数、类型、返回值但是函数名字依然不变。
b.就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。
具体可前往C++中重载、重写(覆盖)的区别实例分析查看
抽象类和接口的区别
a.一个类只能继承单个类,但是可以实现多个接口
b.抽象类中可以有构造方法,接口中不能有构造方法
c.抽象类中的所有方法并不一定要是抽象的,你可以选择在抽象类中实现一些基本的方法。而接口要求所有的方法都必须是抽象的
d.抽象类中可以包含静态方法,接口中不可以
e.抽象类中可以有普通成员变量,接口中不可以
解析XML的几种方式的原理与特点:DOM、SAX、PULL
a.DOM:消耗内存:先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。要是数据过大,手机不够牛逼,可能手机直接死机
b.SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
c.PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。
wait()和sleep()的区别
sleep来自Thread类,和wait来自Object类
调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒
JAVA 中堆和栈的区别,说下java 的内存机制
a.基本数据类型比变量和对象的引用都是在栈分配的
b.堆内存用来存放由new创建的对象和数组
c.类变量(static修饰的变量),程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中
d.实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”,实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存
e.局部变量: 由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放
JAVA多态的实现原理
a.抽象的来讲,多态的意思就是同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
b.实现的原理是动态绑定,程序调用的方法在运行期才动态绑定,追溯源码可以发现,JVM 通过参数的自动转型来找到合适的办法。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
一、JAVA基础篇-概念
1.简述你所知道的Linux:
Linux起源于1991年,1995年流行起来的免费操作系统,目前, Linux是主流的服务器操作系统, 广泛应用于互联网、云计算、智能手机(Android)等领域。由于Java主要用于服务器端的开发,因此Java应用的部署环境有很多为Linux。
Windows操作系统的目录结构,是以盘符为单位,C盘、D盘、E盘等等,数据存储在各个盘符之下,而Linux操作系统最顶层只有一个根目录root,所有文件都存储在这一个根目录之下。
Linux不像Windows的图形操作界面,是通过命令的方式进行操作,常用命令有:
a . pwd:用于显示当前工作目录;
b . ls:用于查看当前工作目录内容;
c . cd:用于改变当前工作目录。
2.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。
Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
3.JDK、JRE、JVM关系是什么?
JDK(Java Development Kit)即为Java开发工具包,包含编写Java程序所必须的编译、运行等开发工具以及JRE。开发工具如:用于编译java程序的javac命令、用于启动JVM运行java程序的java命令、用于生成文档的javadoc命令以及用于打包的jar命令等等。
JRE(Java Runtime Environment)即为Java运行环境,提供了运行Java应用程序所必须的软件环境,包含有Java虚拟机(JVM)和丰富的系统类库。系统类库即为java提前封装好的功能类,只需拿来直接使用即可,可以大大的提高开发效率。
JVM(Java Virtual Machines)即为Java虚拟机,提供了字节码文件(.class)的运行环境支持。
简单说,就是JDK包含JRE包含JVM。
4.Java支持的数据类型有哪些?什么是自动拆装箱?
基本数据类型:
整数值型:byte,short,int,long,
字符型:char
浮点类型:float,double
布尔型:boolean
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。
首先知道String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。String类还是final修饰的。
而包装类就属于引用类型,自动装箱和拆箱就是基本类型和引用类型之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,泛型的限定类型只能是对应的包装类型。
5.面向对象是什么?
面向对象是一种思想,世间万物都可以看做一个对象,这里只讨论面向对象编程(OOP),Java是一个支持并发、基于类和面向对象的计算机编程语言,面向对象软件开发的优点:
代码开发模块化,更易维护和修改;
代码复用性强;
增强代码的可靠性和灵活性;
增加代码的可读性。
- 面向对象的四大基本特性:
抽象:提取现实世界中某事物的关键特性,为该事物构建模型的过程。对同一事物在不同的需求下,需要提取的特性可能不一样。得到的抽象模型中一般包含:属性(数据)和操作(行为)。这个抽象模型我们称之为类。对类进行实例化得到对象。
封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或者子类必须的属性和操作。类封装的实现依赖类的修饰符(public、protected和private等)
继承:对现有类的一种复用机制。一个类如果继承现有的类,则这个类将拥有被继承类的所有非私有特性(属性和操作)。这里指的继承包含:类的继承和接口的实现。
多态:多态是在继承的基础上实现的。多态的三个要素:继承、重写和父类引用指向子类对象。父类引用指向不同的子类对象时,调用相同的方法,呈现出不同的行为;就是类多态特性。多态可以分成编译时多态和运行时多态。
抽象、封装、继承和多态是面向对象的基础。在面向对象四大基础特性之上,我们在做面向对象编程设计时还需要遵循有一些基本的设计原则。
- 面向对象的七大设计原则:
SOLID原则(单一职责原则、开放关闭原则、里氏替换原则、接口隔离原则和依赖倒置原则)
迪米特法则
组合优于继承原则(合成复用原则)。
在遵循这些面向对象设计原则基础上,前辈们总结出一些解决不同问题场景的设计模式,以四人帮的gof23最为知名。
- 24种设计模式(gof23+1):
创建型模式:
1.简单工厂模式(不包含在gof23中)
2.工厂模式
3.抽象工厂模式
4.单例模式
5.原型模式
创建者模式
6.结构型模式:
7.组合模式
8.装饰者模式
9.外观模式
10.适配器模式
11.代理模式
12.享元模式
13.桥接模式
行为型模式:
14.观察者模式
15.策略模式
16.状态模式
17.中介模式
18.模板方法
19.命令模式
20.备忘录模式
21.访问者模式
22.解释器模式
23.迭代器模式
24.职责链模式
这里只是简单描述了定义和特征以及设计模式的关系,具体细节不讨论。
6.请写出下面几个表达式的结果,答案可以用10进制或16进制书写
1). 0xaa | 0x55
2). 15 & 240
3). 10 ^ 12
4). -2 >> 1
5). -2 >>> 1
1). 分析:十六进制数用0x……来表示,后面一个十六进制位是四位,两个十六进制位为一个字节,最多后面可以有8个十六进制位,32个字节,如:0xFFFFFFFF。 或(“ | ”)运算,全0为0,其他为1。
所以:0xaa 用二进制表示为 10101010 ,0x55 用二进制表示为 01010101 ,按位或之后为 11111111 ,十进制数为255,十六进制数为 0xFF 。
2). 分析:10进制转换成2进制,用该数字除以2,记录商和余数,利用商再次除以2,记录商和余数……直到上为0或余数为0停止,余数逆序组成二进制的从低到高位(最后的余数为二进制最低位)。与(“ & ”)运算,全1为1,其他为0 。
所以: 15 等于1111 ,240等于 11110000,15前面用0补齐为00001111 ,按位与之后为 00000000 ,即结果为0
3). 分析: 亦或(“ ^ ”)运算,相同取0,不同取1 。
所以:1010 ^ 1100 =0110 , 十进制表示为6,十六进制表示为 0x06 。
4). 分析: 带符号右移(“ >> ”),即有符号位时,负数符号位补1,正数符号位补0, -2 的二进制求法是正数取反加1,因此 2 的二进制表示为0000 0000 0000 0000 0000 0000 0000 0010 ,取反加一为
1111 1111 1111 1111 1111 1111 1111 1110 ,即 -2 的二进制表示。
注: >> , << , >>> , 运算符只针对int型和long型,byte ,short ,char型需要转换成Int型在进行操作。
所以: 带符号右移之后为 1111 1111 1111 1111 1111 1111 1111 1111 ,除符号位之外,减一取反,得到带符号十进 制数为 -1 。
5). 分析:无符号右移 (“ >>> ”) ,即无论正负数,右移之后符号位均补 0 。
所以: -2 的二进制无符号右移一位之后为 0111 1111 1111 1111 1111 1111 1111 1111,即 2^31 - 1,二的三十一次方减一。
注:右移和无符号右移主要区别就在于左面最高位补 0 还是补 1 的问题,无符号右移任何时候最高位都补 0 , 有符号右移则是正数补 0 ,负数补 1 。(没有无符号左移!)。
7.&和&&的区别?
&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
8.什么是值传递和引用传递?
值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。
一般认为,java内的传递都是值传递. java中实例对象的传递是引用传递 。
9.是否可以在static环境中访问非static变量?
static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
10.Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。
11.Java中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?
当新对象被创建的时候,构造方法会被调用。每一个类都有构造方法。在程序员没有给类提供构造方法的情况下,Java编译器会为这个类创建一个默认的构造方法。
Java中构造方法重载和方法重载很相似。可以为一个类创建多个构造方法。每一个构造方法必须有它自己唯一的参数列表。
Java不支持像C++中那样的复制构造方法,这个不同点是因为如果你不自己写构造方法的情况下,Java不会创建默认的复制构造方法。
12.Java支持多继承么?
Java中类不支持多继承,只支持单继承(即一个类只有一个父类)。 但是java中的接口支持多继承,,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。
13.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、”hello”和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemoryError。
14.接口和抽象类的区别是什么?
从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:
接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
类可以实现很多个接口,但是只能继承一个抽象类
类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
抽象类可以在不提供接口方法实现的情况下实现接口。
Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
也可以参考JDK8中抽象类和接口的区别。
15.用最有效率的方法计算2乘以8?
答: 2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。
16.手写单例模式(饿汉和饱汉模式)和工厂模式?
(1)单例饿汉模式://饿汉式单例类.在类初始化时,已经自行实例化
2 public class Singleton1 {
3 //私有的默认构造子
4 private Singleton1() {}
5 //已经自行实例化
6 private static final Singleton1 single = new Singleton1();
7 //静态工厂方法
8 public static Singleton1 getInstance() {
9 return single;
10 }
11 }
(2)懒汉模式://懒汉式单例类.在第一次调用的时候实例化
2 public class Singleton2 {
3 //私有的默认构造子
4 private Singleton2() {}
5 //注意,这里没有final
6 private static Singleton2 single=null;
7 //静态工厂方法
8 public synchronized static Singleton2 getInstance() {
9 if (single == null) {
10 single = new Singleton2();
11 }
12 return single;
13 }
14 }
(3)工厂模式:
interface IFactory{
public IProduct createProduct();}
Class Factory implements IFactory{
public IProduct createProduct(){return new Product();}}
Public class client{
Public Static void main (String [] args){IFactory factory=new Factory();
IProduct product=factory.createProduct();
product.ProductMethod();}}
17.String和StringBuilder、StringBuffer的区别?
Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
二、JAVA基础篇-集合与数组
18.Java集合框架是什么?说出一些集合框架的优点?
每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector、Stack、HashTable和Array。随着集合的广泛使用,Java1.2提出了囊括所有集合接口、实现和算法的集合框架。在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了很久。它还包括在Java并发包中,阻塞接口以及它们的实现。集合框架的部分优点如下:
(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。
(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。
(3)通过使用JDK附带的集合类,可以降低代码维护成本。
(4)复用性和可操作性。
19.集合框架中的泛型有什么优点?
Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。
20.Java集合框架的基础接口有哪些?
Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。## 标题 ##
Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。
List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。
Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
21.为何Collection不从Cloneable和Serializable接口继承?
Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。
当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。
在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。
22.为何Map接口不继承Collection接口?
尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。
如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。
23.什么是迭代器(Iterator)?
Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object Obj)删除,可以通过迭代器的remove()方法删除。
24.Iterator和ListIterator的区别是什么?
下面列出了他们的区别:
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
25.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个ConcurrentModification异常。
在java.util包下的都是快速失败。
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出ConcurrentModification异常。
在java.util.concurrent包下的全是安全失败的。
26.Java中的HashMap的工作原理是什么?
我们知道在Java中最常用的两种结构是数组和模拟指针(引用),几乎所有的数据结构都可以利用这两种来组合实现,HashMap也是如此。实际上HashMap是一个“链表散列”,如下是它数据结构:最左侧是一个数组,数组中的每一个元素都是一个链表,链表的每一个元素都是entry。
HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。
27.当两个对象的hashcode相同会发生什么?
因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。
28.如果两个键的hashcode相同,你如何获取值对象?
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。
29.hashCode()和equals()方法有何重要性?
HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。equals()和hashCode()的实现应该遵循以下规则:
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。
具体可以参考 http://blog.csdn.net/javazejian/article/details/51348320
30.HashMap和Hashtable有什么区别?
1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。
31.如何决定选用HashMap还是TreeMap?
对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。
32.ArrayList和Vector有何异同点?
ArrayList和Vector在很多时候都很类似。
(1)两者都是基于索引的,内部由一个数组支持。
(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。
(3)ArrayList和Vector的迭代器实现都是fail-fast的。
(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。
以下是ArrayList和Vector的不同点。
(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。
(2)ArrayList比Vector快,它因为有同步,不会过载。
(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。
33.Array和ArrayList有何区别?什么时候更适合用Array?
Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。
(1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。
(2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。
(3)如果你要使用多维数组,使用[][]比List