【转】死磕面试:2019最全Java面试题

面试专题 · 2019-05-29 · 151 人浏览

转载自:https://blog.csdn.net/crazymakercircle/article/details/82555359。其实原文也是转载的,但是原文没有写明转载自何处,所以我只能把我转载自何处写在这里。

如下题目如果全部能搞定,至少涨薪50%:
[第一部分] 面试要领
[第1题] 流程必知必会
[第2题] JDK源码
[第二部分] 类和对象
[第二篇] 面向对象基础
[第1题] 面向对象是什么?
[第2题] 类加载的过程
[第3题] 类加载器有哪些
[第4题] 双亲委派模式
[第5题] 编写自己的类加载器
[第6题] 对象的创建
[第7题] jvm的优化
[第8题] 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
[第9题] JDK和JRE的区别是什么?
[第10题] ”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?
[第11题] 是否可以在static环境中访问非static变量?
[第12题] Java支持的数据类型有哪些?什么是自动拆装箱?
[第13题] Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
[第14题] Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
[第15题] Java支持多继承么?
[第16题] 接口和抽象类的区别是什么?
[第17题] 10.什么是值传递和引用传递?
[第18题] 我们能在 Switch 中使用 String 吗?
[第19题] 20.Java 中的构造器链是什么?
[第20题] 64 位 JVM 中,int 的长度是多数?
[第21题] 32 位和 64 位的 JVM,int 类型变量的长度是多数?
[第22题] “a==b”和”a.equals(b)”有什么区别?
[第23题] a.hashCode() 有什么用?与 a.equals(b) 有什么关系?
[第24题] final、finalize 和 finally 的不同之处?
[第25题] Java 中的编译期常量是什么?使用它又什么风险?
[第26题] 用最有效率的方法计算2乘以8?
[第三篇] JVM
[第1题] 要求
[第2题] JVM的内存结构
[第3题] .Java的内存模型以及GC算法
[第4题] Java中垃圾回收有什么目的?什么时候进行垃圾回收?
[第5题] System.gc()和Runtime.gc()会做什么事情?
[第6题] finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?
[第7题] 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
[第8题] Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?
[第9题] 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?
[第10题] 在Java中,对象什么时候可以被垃圾回收?
[第11题] JVM的永久代中会发生垃圾回收么?
[第12题] Serial 与 Parallel GC之间的不同之处?
[第13题] Java 中 WeakReference 与 SoftReference的区别?
[第14题] JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用?
[第15题] 怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位?
[第16题] 32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?
[第17题] JRE、JDK、JVM 及 JIT 之间有什么不同?
[第18题] 解释 Java 堆空间及 GC?
[第19题] 你能保证 GC 执行吗?
[第20题] 怎么获取 Java 程序使用的内存?堆使用的百分比?
[第21题] Java 中堆和栈有什么区别?(答案)
[第22题] 解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
[第23题] 动态代理的两种方式,以及区别
[第四篇] 异常处理
[第1题] Java中的两种异常类型是什么?他们有什么区别?
[第2题] Java中Exception和Error有什么区别?
[第3题] throw和throws有什么区别?
[第4题] 异常处理的时候,finally代码块的重要性是什么?(译者注:作者标题的序号弄错了)
[第5题] 异常处理完成以后,Exception对象会发生什么变化?
[第6题] finally代码块和finalize()方法有什么区别?
[第7题] .Java内存泄露的问题调查定位:
[第五篇] 不可变对象
[第1题] 什么是不可变对象(immutable object)?Java 中怎么创建一个不可变对象?
[第2题] 我们能创建一个包含可变对象的不可变对象吗?
[第六篇] 数据类型
[第1题] 请写出下面几个表达式的结果,答案可以用10进制或16进制书写
[第2题] &和&&的区别?
[第3题] Java 中应该使用什么数据类型来代表价格?
[第4题] 怎么将 byte 转换为 String?
[第5题] Java 中 bytes 与其他类型的转换?
[第6题] 我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象?
[第7题] 存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为 C 么?如 C = (C) B;
[第8题] 哪个类包含 clone 方法?是 Cloneable 还是 Object?
[第9题] a = a + b 与 a += b 的区别
[第10题] 我能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?
[第11题] 3*0.1 == 0.3 将会返回什么?true 还是 false?
[第12题] int 和 Integer 哪个会占用更多的内存?
[第13题] 为什么 Java 中的 String 是不可变的(Immutable)?
[第14题] String和StringBuilder、StringBuffer的区别?
[第三部分] 集合类
[第15题] Java集合类框架的基本接口有哪些?
[第16题] 为什么集合类没有实现Cloneable和Serializable接口?
[第17题] 什么是迭代器(Iterator)?
[第18题] Iterator和ListIterator的区别是什么?
[第19题] 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
[第20题] Java中的HashMap的工作原理是什么?
[第21题] 当两个对象的hashcode相同会发生什么?
[第22题] hashCode()和equals()方法的重要性体现在什么地方?
[第23题] HashMap的扩容机制以及默认大小为何是2次幂 原
[第24题] HashMap的长度为什么设置为2的n次方?
[第25题] HashMap和Hashtable有什么区别?
[第26题] 如何决定选用HashMap还是TreeMap?
[第27题] HashMap,HashTable,ConcurrentHashMap的区别
[第28题] 极高并发下HashTable和ConcurrentHashMap哪个性能更好,为什么,如何实现的。
[第29题] ArrayList和数组(Array)和有什么区别?什么时候应该使用Array而不是ArrayList?
[第30题] ArrayList和LinkedList有什么区别?
[第31题] ArrayList和Vector有何异同点?
[第32题] Comparable和Comparator接口是干什么的?列出它们的区别。
[第33题] 什么是Java优先级队列(Priority Queue)?
[第34题] 30.你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?
[第35题] 如何权衡是使用无序的数组还是有序的数组?
[第36题] Java集合类框架的最佳实践有哪些?
[第37题] Enumeration接口和Iterator接口的区别有哪些?
[第38题] HashSet和TreeSet有什么区别?
[第39题] List、Set、Map 和 Queue 之间的区别
[第40题] poll() 方法和 remove() 方法的区别?
[第41题] Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?
[第42题] ArrayList 与 LinkedList 的不区别?
[第43题] 用哪两种方式来实现集合的排序?
[第44题] Java 中怎么打印数组?(answe
[第45题] Java 中的 LinkedList 是单向链表还是双向链表?
[第46题] Java 中的 TreeMap 是采用什么树实现的?
[第47题] Hashtable 与 HashMap 有什么不同之处?
[第48题] Java 中的 HashSet,内部是如何工作的?
[第49题] 写一段代码在遍历 ArrayList 时移除一个元素?
[第50题] 我们能自己写一个容器类,然后使用 for-each 循环码?
[第51题] ArrayList 和 HashMap 的默认大小是多数?
[第52题] 集合框架中的泛型有什么优点?
[第53题] 为何Collection不从Cloneable和Serializable接口继承?
[第54题] 为何Map接口不继承Collection接口?
[第四部分] 流
[第55题] 给定一个txt文件,如何得到某字符串出现的次数
[第56题] 讲讲IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞
[第57题] 什么是流,按照传输单位,分成哪两种流,并且他们的父类叫什么流是指数据的传输
[第58题] 什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征
[第59题] String编码UTF-8和GBK的区别
[第60题] 什么时候使用字节流,什么时候    使用字符流
[第61题] 递归读取文件夹的文件,代码怎么实现
[第62题] 什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别
[第63题] BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法
[第64题] 如果我要对字节流进行大量的从硬盘读取,要用那个流,为什么
[第65题] 如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么
[第66题] 怎么样把我们控制台的输出改成输出到一个文件里面,这个技术叫什么
[第67题] 怎么样把输出字节流转换成输出字符流,说出它的步骤
[第68题] 把包括基本类型在内的数据和字符串按顺序输出到数据源,或者按照顺序从数据源读入,一般用哪两个流
[第69题] 把一个对象写入数据源或者从一个数据源读出来,用哪两个流
[第70题] 什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作
[第71题] 在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用
[第72题] OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思
[第73题] IO流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?
[第74题] 写一段代码读取一个序列化的对象一般使用哪种Stream?
[第75题] io流怎样读取文件的?
[第76题] FileInputStream 创建详情,就是怎样的创建不报错,它列出了几种形式!
[第77题] 用io流中的技术,指定一个文件夹的目录,获取此目录下的所有子文件夹路径
[第78题] 请问你在什么情况下会在你得java代码中使用可序列化? 如何实现java序列化?
[第79题] 问:简单说说你平时使用的 Java IO 流中涉及到了哪些设计策略和设计模式?
[第80题] 问:什么是缓冲区?有什么作用?
[第81题] 问:字节流和字符流哪个好?怎么选择?
[第82题] 说说对IO流的理解
[第五部分] 设计模式
[第七篇] 单例模式
[第1题] 手写单例模式(饿汉和饱汉模式)和工厂模式?
[第2题] 策略模式
[第3题] 工厂模式
[第4题] AOP与IOC的概念(即spring的核心)
[第六部分] 算法
[第5题] 冒泡排序
[第6题] 二分查找
[第7题] 数据结构必知必会
[第8题] 你知道索引使用的是哪种数据结构实现吗?
[第七部分] 多线程
[第9题] 说说对多线程的理解
[第10题] 进程和线程的区别是什么?
[第11题] 创建线程有几种不同的方式?你喜欢哪一种?为什么?
[第12题] 概括的解释下线程的几种可用状态。
[第13题] 同步方法和同步代码块的区别是什么?
[第14题] synchronized关键字的用法,优缺点
[第15题] Lock接口有哪些实现类,使用场景是什么
[第16题] ABC三个线程如何保证顺序执行
[第17题] 在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
[第18题] 悲观锁、乐观锁的优缺点,CAS有什么缺陷,该如何解决
[第19题] ABC三个线程如何保证顺序执行
[第20题] 假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
[第21题] 线程池的问题
[第22题] 线程的状态都有哪些(五大状态)
[第23题] 什么是死锁(deadlock)?
[第24题] 如何确保N个线程可以访问N个资源同时又不导致死锁?
[第25题] 什么是线程局部变量?
[第26题] 用 wait-notify 写一段代码来解决生产者-消费者问题?
[第27题] 用 Java 写一个线程安全的单例模式(Singleton)?
[第28题] Java 中 sleep 方法和 wait 方法的区别?
[第29题] notify()和notifyAll()的区别
[第30题] ThreadLocal的了解,实现原理。
[第31题] Java 中 ++ 操作符是线程安全的吗?
[第八部分] 操作系统
[第32题] 简述你所知道的Linux:
[第九部分] web
[第33题] servlet执行流程
[第34题] springMVC的执行流程
[第35题] Struts工作流程
[第36题] 对ajax的理解
[第37题] Tomcat服务器优化
[第38题] HTTP协议
[第39题] TCP/UDP协议
[第40题] Spring使用了哪些设计模式
[第41题] session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。
[第42题] struts1和struts2的区别
[第43题] struts2和springMVC的区别
[第44题] spring框架中需要引用哪些jar包,以及这些jar包的用途
[第45题] srpingMVC的原理
[第46题] springMVC注解的意思
[第47题] spring中beanFactory和ApplicationContext的联系和区别
[第48题] spring注入的几种方式
[第49题] spring如何实现事物管理的
[第50题] springIOC和AOP的原理
[第51题] hibernate中的1级和2级缓存的使用方式以及区别原理
[第52题] spring中循环注入的方式
[第十部分] 数据库
[第53题] 高并发处理
[第54题] 事物的理解
[第55题] 索引的分类
[第56题] 存储引擎的 InnoDB 与 MyISAM
[第57题] 常见的数据库优化手段
[第58题] 索引的优缺点,什么字段上建立索引
[第59题] 数据库连接池
[第60题] 避重就轻
[第十一部分] end

 

[第一部分] 面试要领

我们今天面对这样的场景:某一天,周边一个平时默默无闻的兄弟,神秘的对你透露一个小秘密:他要跳槽了,offer已经到手,涨薪50%有几千甚至上万。这种场景下,你是如何反应的呢?
你表面平静,内心却心潮澎湃。有一个问题一直在想:有什么法子,让自己也能涨薪几千,多些钱来交房贷和车贷呢?

秘笈其实很简单,也很老土:就是除了跟进高大上的新技术,更重要的是需要扎好基本功。

面试题表面上天马行空,毫无章法。但是,深入研究,其实是对基本功最好的检验。深厚的基本功,是高薪的必要条件,这一点:切记、切记。

 


[第1题] 流程必知必会

  1、简历要用心准备好,个人信息,特别是联系方式一定要清晰明确,自身掌握的技能要完成清晰,项目经历最好按照时间顺序,说明本人在项目中的职责,完成的工作,有什么样的提升或收获;

  2、一般面试流程是电面=》HR现场面=》技术面=》结果,并不是每一个面试结果就能立马有结果,所以当面试官说回去等消息的时候,并不代表没有机会,有时候需要讨论筛选才能最终确定人选。

  3、关于自我介绍,最好简明扼要,能体现自身的特点,表达流畅、自信,提前最好准备;

  4、准备好扎实的基础知识,以及对经历过的项目要有足够的认识,每一个项目都是一次学习、提升的机会,一般JAVA集合类是考察的重点;

  5、一般好一点的面试官会顺着知识点逐渐深入或者逐渐扩展,所以对于知识点的掌握最好全面深入,不要走马观花式的学习;

6、当遇到一些设计类的问题时,一般面试官考察的是你的思路,对问题的应变能力,对于事物观察的点;

[第2题] JDK源码

要想拿高工资,JDK源码不可不读。总结一下比较重要的源码:

1、List、Map、Set实现类的源代码
2、ReentrantLock、AQS的源代码

3 AtomicInteger的实现原理,主要能说清楚CAS机制并且AtomicInteger是如何利用CAS机制实现的

4 线程池的实现原理

5 Object类中的方法以及每个方法的作用

这些其实要求蛮高的,学习源码,短时间来说,是一件费事费力的事情,从长远角度来看----这是非常值得的,不仅仅是为了应付面试。


[第二部分] 类和对象

[第二篇] 面向对象基础

[第1题] 面向对象是什么?

面向对象是一种思想,世间万物都可以看做一个对象,这里只讨论面向对象编程(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.职责链模式
这里只是简单描述了定义和特征以及设计模式的关系,具体细节不讨论。

[第2题] 类加载的过程

参考:

https://blog.csdn.net/javazejian/article/details/73413292#启动bootstrap类加载器

a) 遇到一个新的类时,首先会到方法区去找class文件,如果没有找到就会去硬盘中找class文件,找到后会返回,将class文件加载到方法区中,在类加载的时候,静态成员变量会被分配到方法区的静态区域,非静态成员变量分配到非静态区域,然后开始给静态成员变量初始化,赋默认值,赋完默认值后,会根据静态成员变量书写的位置赋显示值,然后执行静态代码。当所有的静态代码执行完,类加载才算完成。

 

 

 

 

 

加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象

验证:目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。

解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用,如需更详细了解,可参考《深入Java虚拟机》)。

初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。


这便是类加载的5个过程,而类加载器的任务是根据一个类的全限定名来读取此类的二进制字节流到JVM中,然后转换为一个与目标类对应的java.lang.Class对象实例,在虚拟机提供了3种类加载器,引导(Bootstrap)类加载器、扩展(Extension)类加载器、系统(System)类加载器(也称应用类加载器)

 


[第3题] 类加载器有哪些


我们进一步了解类加载器间的关系(并非指继承关系),主要可以分为以下4点

启动类加载器,由C++实现,没有父类。

拓展类加载器(ExtClassLoader),由Java语言实现,父类加载器为null

系统类加载器(AppClassLoader),由Java语言实现,父类加载器为ExtClassLoader

自定义类加载器,父类加载器肯定为AppClassLoader。

启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。
扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。
也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
  在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它。


public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException { FileClassLoader loader1 = new FileClassLoader(rootDir); System.out.println("自定义类加载器的父加载器: "+loader1.getParent()); System.out.println("系统默认的AppClassLoader: "+ClassLoader.getSystemClassLoader()); System.out.println("AppClassLoader的父类加载器: "+ClassLoader.getSystemClassLoader().getParent()); System.out.println("ExtClassLoader的父类加载器: "+ClassLoader.getSystemClassLoader().getParent().getParent()); /** 输出结果: 自定义类加载器的父加载器: sun.misc.Launcher$AppClassLoader@29453f44 系统默认的AppClassLoader: sun.misc.Launcher$AppClassLoader@29453f44AppClassLoader的父类加载器: sun.misc.Launcher$ExtClassLoader@6f94fa3e ExtClassLoader的父类加载器: null */ }

[第4题] 双亲委派模式

双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码,类加载器间的关系如下

 

 

 

 

 

 

 

 

 

 

双亲委派模式是在Java 1.2后引入的,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成,这不就是传说中的实力坑爹啊?那么采用这种模式有啥用呢?

双亲委派模式优势

采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。可能你会想,如果我们在classpath路径下自定义一个名为java.lang.SingleInterge类(该类是胡编的)呢?该类并不存在java.lang中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为java.lang是核心API包,需要访问权限,强制加载将会报出如下异常

java.lang.SecurityException: Prohibited package name: java.lang
1
所以无论如何都无法加载成功的。

 

 


[第5题] 编写自己的类加载器

场景:

那么编写自定义类加载器的意义何在呢?

当class文件不在ClassPath路径下,默认系统类加载器无法找到该class文件,在这种情况下我们需要实现一个自定义的ClassLoader来加载特定路径下的class文件生成class对象。

当一个class文件是通过网络传输并且可能会进行相应的加密操作时,需要先对class文件进行相应的解密后再加载到JVM内存中,这种情况下也需要编写自定义的ClassLoader并实现相应的逻辑。

当需要实现热部署功能时(一个class文件通过不同的类加载器产生不同class对象从而实现热部署功能),需要实现自定义ClassLoader的逻辑。

方法:
实现自定义类加载器需要继承ClassLoader或者URLClassLoader,继承ClassLoader则需要自己重写findClass()方法并编写加载逻辑,继承URLClassLoader则可以省去编写findClass()方法以及class文件加载转换成字节码流的代码。

 

 

 


[第6题] 对象的创建

a) 遇到一个新类时,会进行类的加载,定位到class文件

b) 对所有静态成员变量初始化,静态代码块也会执行,而且只在类加载的时候执行一次

c) New 对象时,jvm会在堆中分配一个足够大的存储空间

d) 存储空间清空,为所有的变量赋默认值,所有的对象引用赋值为null

e) 根据书写的位置给字段一些初始化操作

f) 调用构造器方法(没有继承)

[第7题] jvm的优化

a) 设置参数,设置jvm的最大内存数

b) 垃圾回收器的选择


[第8题] 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。

Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

[第9题] JDK和JRE的区别是什么?

Java运行时环境(JRE)是将要执行Java程序的Java虚拟机。它同时也包含了执行applet需要的浏览器插件。Java开发工具包(JDK)是完整的Java软件开发包,包含了JRE,编译器和其他的工具(比如:JavaDoc,Java调试器),可以让开发者开发、编译、执行Java应用程序。


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。

[第10题] ”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

[第11题] 是否可以在static环境中访问非static变量?

static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。

[第12题] Java支持的数据类型有哪些?什么是自动拆装箱?

Java语言支持的8中基本数据类型是:

byte
short
int
long
float
double
boolean
char
自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer,double转化成double,等等。反之就是自动拆箱。

基本数据类型:
整数值型:byte,short,int,long,
字符型:char
浮点类型:float,double
布尔型:boolean
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。

首先知道String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。String类还是final修饰的。
而包装类就属于引用类型,自动装箱和拆箱就是基本类型和引用类型之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,泛型的限定类型只能是对应的包装类型。

[第13题] Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。

[第14题] Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?

当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,Java编译器会为这个类创建一个默认的构造函数。

Java中构造函数重载和方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数必须有它自己唯一的参数列表。

Java不支持像C++中那样的复制构造函数,这个不同点是因为如果你不自己写构造函数的情况下,Java不会创建默认的复制构造函数。

[第15题] Java支持多继承么?

不支持,Java不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。

[第16题] 接口和抽象类的区别是什么?

Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:

接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
类可以实现很多个接口,但是只能继承一个抽象类
类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
抽象类可以在不提供接口方法实现的情况下实现接口。
Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
也可以参考JDK8中抽象类和接口的区别

[第17题] 10.什么是值传递和引用传递?

对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。

对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。

[第18题] 我们能在 Switch 中使用 String 吗?

从 Java 7 开始,我们可以在 switch case 中使用字符串,但这仅仅是一个语法糖。内部实现在 switch 中使用字符串的 hash code。

[第19题] 20.Java 中的构造器链是什么?

当你从一个构造器中调用另一个构造器,就是Java 中的构造器链。这种情况只在重载了类的构造器的时候才会出现。


[第20题] 64 位 JVM 中,int 的长度是多数?

 

Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的Java 虚拟机中,int 类型的长度是相同的。


[第21题]  32 位和 64 位的 JVM,int 类型变量的长度是多数?

 

32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4 个字节。


[第22题]  “a==b”和”a.equals(b)”有什么区别?

 

如果 a 和 b 都是对象,则 a==b 是比较两个对象的引用,只有当 a 和 b 指向的是堆中的同一个对象才会返回 true,而 a.equals(b) 是进行逻辑比较,所以通常需要重写该方法来提供逻辑一致性的比较。例如,String 类重写 equals() 方法,所以可以用于两个不同对象,但是包含的字母相同的比较。

 

[第23题] a.hashCode() 有什么用?与 a.equals(b) 有什么关系?

 

hashCode() 方法是相应对象整型的 hash 值。它常用于基于 hash 的集合类,如 Hashtable、HashMap、LinkedHashMap等等。它与 equals() 方法关系特别紧密。根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code。

[第24题] final、finalize 和 finally 的不同之处?

 

final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变。finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证。finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。

 

[第25题] Java 中的编译期常量是什么?使用它又什么风险?

 

公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里的 public 可选的。实际上这些变量在编译时会被替换掉,因为编译器知道这些变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端仍然在使用老的值,甚至你已经部署了一个新的jar。为了避免这种情况,当你在更新依赖 JAR 文件时,确保重新编译你的程序。

[第26题] 用最有效率的方法计算2乘以8?

答: 2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。
[第三篇] JVM


[第1题] 要求

Java虚拟机

出乎意料,Java虚拟机应该是很重要的一块内容,结果在这几家公司中被问到的概率几乎为0。要知道,去年可是花了大量的时间去研究Java虚拟机的,光周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》,就读了不下五遍。

言归正传,虽然Java虚拟机没问到,但我觉得还是有必要研究的,就简单地列一个提纲吧,谈谈Java虚拟机中比较重要的内容:

(1)Java虚拟机的内存布局

(2)GC算法及几种垃圾收集器

(3)类加载机制,也就是双亲委派模型

(4)Java内存模型

(5)happens-before规则

(6)volatile关键字使用规则


[第2题] JVM的内存结构

  答:主要分为三大块 堆内存、方法区、栈;栈又分为JVM栈、本地方法栈

    堆(heap space),堆内存是JVM中最大的一块,有年轻代和老年代组成,而年轻代又分为三分部分,Eden区,From Survivor,To Survivor,默认情况下按照8:1:1来分配

    方法区(Method area),存储类信息、常量、静态变量等数据,是线程共享的区域

    程序计数器(Program counter Register),是一块较小的内存空间,是当前线程所执行的字节码的行号指示器

    JVM栈(JVM stacks),也是线程私有的,生命周期与线程相同,每个方法被执行时都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息

    本地方法栈(Native Mthod Stacks),为虚拟机使用的native方法服务

[第3题] .Java的内存模型以及GC算法

[第4题] Java中垃圾回收有什么目的?什么时候进行垃圾回收?
垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。
[第5题] System.gc()和Runtime.gc()会做什么事情?
这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。

 


[第6题] finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?
在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。一般建议在该方法中释放对象持有的资源。
[第7题] 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
不会,在下一个垃圾回收周期中,这个对象将是可被回收的。
[第8题] Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?
JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
[第9题] 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?
吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。
[第10题] 在Java中,对象什么时候可以被垃圾回收?
当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了。
[第11题] JVM的永久代中会发生垃圾回收么?
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区
(译者注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)

[第12题] Serial 与 Parallel GC之间的不同之处?

 

Serial 与 Parallel 在GC执行的时候都会引起 stop-the-world。它们之间主要不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而 parallel 收集器使用多个 GC 线程来执行。

[第13题] Java 中 WeakReference 与 SoftReference的区别?

 

虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。


[第14题] JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用?

 

当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。

 

[第15题] 怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位?

 

你可以检查某些系统属性如 sun.arch.data.model 或 os.arch 来获取该信息。

 

[第16题] 32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

 

理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。

 

[第17题] JRE、JDK、JVM 及 JIT 之间有什么不同?

 

JRE 代表 Java 运行时(Java run-time),是运行 Java 引用所必须的。JDK 代表 Java 开发工具(Java development kit),是 Java 程序的开发工具,如 Java 编译器,它也包含 JRE。JVM 代表 Java 虚拟机(Java virtual machine),它的责任是运行 Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将 Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高 Java 应用的性能。

 

[第18题] 解释 Java 堆空间及 GC?

 

当通过 Java 命令启动 Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是 JVM 内部的一个进程,回收无效对象的内存用于将来的分配。

 


[第19题] 你能保证 GC 执行吗?

 

不能,虽然你可以调用 System.gc() 或者 Runtime.gc(),但是没有办法保证 GC 的执行。

 

[第20题] 怎么获取 Java 程序使用的内存?堆使用的百分比?

 

可以通过 java.lang.Runtime 类中与内存相关方法来获取剩余的内存,总内存及最大堆内存。通过这些方法你也可以获取到堆使用的百分比及堆内存的剩余空间。

 

Runtime.freeMemory() 方法返回剩余空间的字节数,Runtime.totalMemory() 方法总内存的字节数,Runtime.maxMemory() 返回最大内存的字节数。

 

[第21题] Java 中堆和栈有什么区别?(答案)

 

JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。


[第22题] 解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。

通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、”hello”和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemoryError。


[第23题] 动态代理的两种方式,以及区别

  答:jdk动态代理和cglib动态代理;

  JDK动态代理只能对实现了接口的类生成代理,而不能针对类;cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明称final,final可以阻止继承和多态;

[第四篇] 异常处理


[第1题] Java中的两种异常类型是什么?他们有什么区别?

Java中有两种异常:受检查的(checked)异常和不受检查的(unchecked)异常。不受检查的异常不需要在方法或者是构造函数上声明,就算方法或者是构造函数的执行可能会抛出这样的异常,并且不受检查的异常可以传播到方法或者是构造函数的外面。相反,受检查的异常必须要用throws语句在方法或者是构造函数上声明。这里有Java异常处理的一些小建议。

[第2题] Java中Exception和Error有什么区别?

Exception和Error都是Throwable的子类。Exception用于用户程序可以捕获的异常情况。Error定义了不期望被用户程序捕获的异常。

[第3题] throw和throws有什么区别?

throw关键字用来在程序中明确的抛出异常,相反,throws语句用来表明方法不能处理的异常。每一个方法都必须要指定哪些异常不能处理,所以方法的调用者才能够确保处理可能发生的异常,多个异常是用逗号分隔的。

[第4题] 异常处理的时候,finally代码块的重要性是什么?(译者注:作者标题的序号弄错了)

无论是否抛出异常,finally代码块总是会被执行。就算是没有catch语句同时又抛出异常的情况下,finally代码块仍然会被执行。最后要说的是,finally代码块主要用来释放资源,比如:I/O缓冲区,数据库连接。

[第5题] 异常处理完成以后,Exception对象会发生什么变化?

Exception对象会在下一个垃圾回收过程中被回收掉。

[第6题] finally代码块和finalize()方法有什么区别?

无论是否抛出异常,finally代码块都会执行,它主要是用来释放应用占用的资源。finalize()方法是Object类的一个protected方法,它是在对象被垃圾回收之前由Java虚拟机来调用的。


[第7题] .Java内存泄露的问题调查定位:
jmap,jstack的使用等等

[第五篇] 不可变对象

[第1题] 什么是不可变对象(immutable object)?Java 中怎么创建一个不可变对象?

不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。详情参见答案,一步一步指导你在 Java 中创建一个不可变的类。

[第2题] 我们能创建一个包含可变对象的不可变对象吗?

是的,我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。最常见的例子就是对象中包含一个日期对象的引用。


[第六篇] 数据类型

[第1题] 请写出下面几个表达式的结果,答案可以用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 。(没有无符号左移!)。

[第2题] &和&&的区别?

&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。


[第3题] Java 中应该使用什么数据类型来代表价格?

 

如果不是特别关心内存和性能的话,使用BigDecimal,否则使用预定义精度的 double 类型。


[第4题] 怎么将 byte 转换为 String?


可以使用 String 接收 byte[] 参数的构造器来进行转换,需要注意的点是要使用的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也可能不同。


[第5题] Java 中 bytes 与其他类型的转换?

 

public class Test {         
    private static ByteBuffer buffer = ByteBuffer.allocate(8);       
    public static void main(String[] args) {   
            
        //测试 int 转 byte   
        int int0 = 234;   
        byte byte0 = intToByte(int0);   
        System.out.println("byte0=" + byte0);//byte0=-22   
        //测试 byte 转 int   
        int int1 = byteToInt(byte0);   
        System.out.println("int1=" + int1);//int1=234   
 
        //测试 int 转 byte 数组   
        int int2 = 1417;   
        byte[] bytesInt = intToByteArray(int2);   
        System.out.println("bytesInt=" + bytesInt);//bytesInt=[B@de6ced   
        //测试 byte 数组转 int   
        int int3 = byteArrayToInt(bytesInt);   
        System.out.println("int3=" + int3);//int3=1417   
            
            
        //测试 long 转 byte 数组   
        long long1 = 2223;   
        byte[] bytesLong = longToBytes(long1);   
        System.out.println("bytes=" + bytesLong);//bytes=[B@c17164   
        //测试 byte 数组 转 long   
        long long2 = bytesToLong(bytesLong);   
        System.out.println("long2=" + long2);//long2=2223   
    }   
        
        
    //byte 与 int 的相互转换   
    public static byte intToByte(int x) {   
        return (byte) x;   
    }   
        
    public static int byteToInt(byte b) {   
        //Java 总是把 byte 当做有符处理;我们可以通过将其和 0xFF 进行二进制与得到它的无符值   
        return b & 0xFF;   
    }   
        
    //byte 数组与 int 的相互转换   
    public static int byteArrayToInt(byte[] b) {   
        return   b[3] & 0xFF |   
                (b[2] & 0xFF) << 8 |   
                (b[1] & 0xFF) << 16 |   
                (b[0] & 0xFF) << 24;   
    }   
    
    public static byte[] intToByteArray(int a) {   
        return new byte[] {   
            (byte) ((a >> 24) & 0xFF),   
            (byte) ((a >> 16) & 0xFF),      
            (byte) ((a >> 8) & 0xFF),      
            (byte) (a & 0xFF)   
        };   
    }    
    //byte 数组与 long 的相互转换   
    public static byte[] longToBytes(long x) {   
        buffer.putLong(0, x);   
        return buffer.array();   
    }   
    public static long bytesToLong(byte[] bytes) {   
        buffer.put(bytes, 0, bytes.length);   
        buffer.flip();//need flip    
        return buffer.getLong();   
    }   
}


[第6题] 我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象?

 

是的,我们可以做强制转换,但是 Java 中 int 是 32 位的,而 byte 是 8 位的,所以,如果强制转化是,int 类型的高 24 位将会被丢弃,byte 类型的范围是从 -128 到 128。


[第7题] 存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为 C 么?如 C = (C) B;

 

可以,向下转型。但是不建议使用,容易出现类型转型异常。


[第8题] 哪个类包含 clone 方法?是 Cloneable 还是 Object?

 

java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义。并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ 或 其他本地语言实现的。

 

[第9题] a = a + b 与 a += b 的区别

 

+= 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两这个整型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。如果加法操作的结果比 a 的最大值要大,则 a+b 会出现编译错误,但是 a += b 没问题,如下:

 

byte a = 127;

byte b = 127;

b = a + b; // error : cannot convert from int to byte

b += a; // ok


[第10题] 我能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗?

不行,你不能在没有强制类型转换的前提下将一个 double 值赋值给 long 类型的变量,因为 double 类型的范围比 long 类型更广,所以必须要进行强制转换。

[第11题]  3*0.1 == 0.3 将会返回什么?true 还是 false?

false,因为有些浮点数不能完全精确的表示出来。

[第12题] int 和 Integer 哪个会占用更多的内存?

Integer 对象会占用更多的内存。Integer 是一个对象,需要存储对象的元数据。但是 int 是一个原始类型的数据,所以占用的空间更少。

[第13题] 为什么 Java 中的 String 是不可变的(Immutable)?

Java 中的 String 不可变是因为 Java 的设计者认为字符串使用非常频繁,将字符串设置为不可变可以允许多个客户端之间共享相同的字符串。更详细的内容参见答案。

[第14题] String和StringBuilder、StringBuffer的区别?

Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。

 

[第三部分] 集合类

Collection集合接口,List、set实现Collection接口,arraylist、linkedlist,vector实现list接口,stack继承vector,Map接口,hashtable、hashmap实现map接口

[第15题] Java集合类框架的基本接口有哪些?

Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:

Collection:代表一组对象,每一个对象都是它的子元素。
Set:不包含重复元素的Collection。
List:有顺序的collection,并且可以包含重复元素。
Map:可以把键(key)映射到值(value)的对象,键不能重复。
[第16题] 为什么集合类没有实现Cloneable和Serializable接口?

集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。有的集合类允许重复的键,有些不允许。

[第17题] 什么是迭代器(Iterator)?

Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object Obj)删除,可以通过迭代器的remove()方法删除。

[第18题] Iterator和ListIterator的区别是什么?

下面列出了他们的区别:
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

[第19题] 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个ConcurrentModification异常。
在java.util包下的都是快速失败。
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出ConcurrentModification异常。
在java.util.concurrent包下的全是安全失败的。

 

[第20题] Java中的HashMap的工作原理是什么?

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。


HashMap是基于哈希表的Map接口的非同步实现,提供所有可选的映射操作,并允许使用null值和null键,不保证映射的顺序;HashMap是一个“链表散列”的数据结构,即数组和链表的结合体;它的底层就是一个数组结构,数组中的每一项又是一个链表,每当新建一个HashMap时,就会初始化一个数组;


  而在JDK8中引入了红黑树的部分,当存入到数组中的链表长度大于(默认)8时,即转为红黑树;利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。本文不再对红黑树展开讨论,

 

 

 

 

 

 

 

 

[第21题] 当两个对象的hashcode相同会发生什么?

因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。


当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。

 

[第22题] hashCode()和equals()方法的重要性体现在什么地方?

Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。
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(),并不意味着oequals(o2)会为true。

 


[第23题] HashMap的扩容机制以及默认大小为何是2次幂 原

[第24题] HashMap的长度为什么设置为2的n次方?
HashMap的Put方法

HashMap的数据结构设计可以参考链接。接下来回顾HashMap的put(Key k, Value v)过程:

(1)对 Key求Hash值,计算出Hash表下标,对应hashCode()方法,所以使用class对象作为Key时需要重写该对象的hashCode()方法与equals()方法。
(2)如果没有碰撞,直接放入桶中,即Hash表数组对应位置的链表表头。
(3)如果碰撞了,若节点已经存在就替换旧值,否则以链表的方式将该元素链接到后面。
(4)如果链表长度超过阀值(TREEIFY_THRESHOLD == 8),就把链表转成红黑树。红黑树我不熟悉,这里不展开讲。
(5)如果桶满了(容量 * 加载因子),就需要resize。

HashMap的扩容机制

假设length为Hash表数组的大小,方法indexFor(int hash, int length)为

indexFor(int hash, int length) {
    return hash % length;
}
在旧数组中同一条Entry链上的元素,在resize过程中,通过重新计算索引位置后,有可能被放到了新数组的不同位置上。JDK8做了一些优化,resize过程中对Hash表数组大小的修改使用的是2次幂的扩展(指长度扩为原来2倍),这样有2个好处。

好处1

在hashmap的源码中。put方法会调用indexFor(int h, int length)方法,这个方法主要是根据key的hash值找到这个entry在Hash表数组中的位置,源码如下:

/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
    // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    return h & (length-1);
}
上述代码也相当于对length求模。 注意最后return的是h&(length-1)。如果length不为2的幂,比如15。那么length-1的2进制就会变成1110。在h为随机数的情况下,和1110做&操作。尾数永远为0。那么0001、1001、1101等尾数为1的位置就永远不可能被entry占用。这样会造成浪费,不随机等问题。 length-1 二进制中为1的位数越多,那么分布就平均。

好处2

以下图为例,其中图(a)表示扩容前的key1和key2两种key确定索引位置的示例,图(b)表示扩容后key1和key2两种key确定索引位置的示例,n代表length。

 

 

 

resize过程中不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“原索引+oldCap”,可以看看下图为16扩充为32的resize示意图(一方面位运算更快,另一方面抗碰撞的Hash函数其实挺耗时的)

[第25题] HashMap和Hashtable有什么区别?

1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
4、Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。


[第26题] 如何决定选用HashMap还是TreeMap?

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。


[第27题] HashMap,HashTable,ConcurrentHashMap的区别

  答:   

  a、HashMap是非线程安全的,HashTable是线程安全的。

  b、HashMap的键和值都允许有null值存在,而HashTable则不行。

  c、因为线程安全的问题,HashMap效率比HashTable的要高。

  HashMap:它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

   Hashtable:Hashtable是遗留类,很多映射的常用功能与HashMap类似,不同的是它承自Dictionary类,并且是线程安全的,任一时间只有一个线程能写Hashtable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。

[第28题] 极高并发下HashTable和ConcurrentHashMap哪个性能更好,为什么,如何实现的。

  答:当然是ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁,而HashTable则使用的是方法级别的锁;因此在新版本中一般不建议使用HashTable,不需要线程安全的场合可以使用HashMap,而需要线程安全的场合可以使用ConcurrentHashMap;

[第29题] ArrayList和数组(Array)和有什么区别?什么时候应该使用Array而不是ArrayList?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。
(1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。
(2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。
(3)如果你要使用多维数组,使用[][]比List

[第30题] ArrayList和LinkedList有什么区别?

ArrayList和LinkedList都实现了List接口,他们有以下的不同点:

ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。

相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。

LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

也可以参考ArrayList vs. LinkedList。


[第31题] 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工具类轻易地获取同步列表和只读列表。

[第32题] Comparable和Comparator接口是干什么的?列出它们的区别。

Java提供了只包含一个compareTo()方法的Comparable接口。这个方法可以个给两个对象排序。具体来说,它返回负数,0,正数来表明输入对象小于,等于,大于已经存在的对象。

Java提供了包含compare()和equals()两个方法的Comparator接口。compare()方法用来给两个输入参数排序,返回负数,0,正数表明第一个参数是小于,等于,大于第二个参数。equals()方法需要一个对象作为参数,它用来决定输入参数是否和comparator相等。只有当输入参数也是一个comparator并且输入参数和当前comparator的排序结果是相同的时候,这个方法才返回true。

[第33题] 什么是Java优先级队列(Priority Queue)?

PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序(natural order)排序的。在创建的时候,我们可以给它提供一个负责给元素排序的比较器。PriorityQueue不允许null值,因为他们没有自然顺序,或者说他们没有任何的相关联的比较器。最后,PriorityQueue不是线程安全的,入队和出队的时间复杂度是O(log(n))。

[第34题] 30.你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?

大O符号描述了当数据结构里面的元素增加的时候,算法的规模或者是性能在最坏的场景下有多么好。
大O符号也可用来描述其他的行为,比如:内存消耗。因为集合类实际上是数据结构,我们一般使用大O符号基于时间,内存和性能来选择最好的实现。大O符号可以对大量数据的性能给出一个很好的说明。

[第35题] 如何权衡是使用无序的数组还是有序的数组?

有序数组最大的好处在于查找的时间复杂度是O(log n),而无序数组是O(n)。有序数组的缺点是插入操作的时间复杂度是O(n),因为值大的元素需要往后移动来给新元素腾位置。相反,无序数组的插入时间复杂度是常量O(1)。

[第36题] Java集合类框架的最佳实践有哪些?

根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList。
有些集合类允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以设置初始容量来避免重新计算hash值或者是扩容。
为了类型安全,可读性和健壮性的原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。
使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。
编程的时候接口优于实现。
底层的集合实际上是空的情况下,返回长度是0的集合或者是数组,不要返回null。
[第37题] Enumeration接口和Iterator接口的区别有哪些?

Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。

[第38题] HashSet和TreeSet有什么区别?

HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。

另一方面,TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。


[第39题] List、Set、Map 和 Queue 之间的区别

 

List 是一个有序集合,允许元素重复。它的某些实现可以提供基于下标值的常量访问时间,但是这不是 List 接口保证的。Set 是一个无序集合。

 

[第40题] poll() 方法和 remove() 方法的区别?

 

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

 

[第41题] Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?

 

PriorityQueue 保证最高或者最低优先级的的元素总是在队列头部,但是 LinkedHashMap 维持的顺序是元素插入的顺序。当遍历一个 PriorityQueue 时,没有任何顺序保证,但是 LinkedHashMap 课保证遍历顺序是元素插入的顺序。

 

[第42题] ArrayList 与 LinkedList 的不区别?

 

最明显的区别是 ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构书链表,不支持随机访问。使用下标访问一个元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。更多细节的讨论参见答案。

 

[第43题] 用哪两种方式来实现集合的排序?

 

你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然后通过 Collections.sort() 来排序。

 

[第44题] Java 中怎么打印数组?(answe

 

你可以使用 Arrays.toString() 和 Arrays.deepToString() 方法来打印数组。由于数组没有实现 toString() 方法,所以如果将数组传递给 System.out.println() 方法,将无法打印出数组的内容,但是 Arrays.toString() 可以打印每个元素。

 

[第45题] Java 中的 LinkedList 是单向链表还是双向链表?

 

是双向链表,你可以检查 JDK 的源码。在 Eclipse,你可以使用快捷键 Ctrl + T,直接在编辑器中打开该类。

 

[第46题] Java 中的 TreeMap 是采用什么树实现的?

 

Java 中的 TreeMap 是使用红黑树实现的。

 

[第47题]  Hashtable 与 HashMap 有什么不同之处?

 

这两个类有许多不同的地方,下面列出了一部分:

 

a) Hashtable 是 JDK 1 遗留下来的类,而 HashMap 是后来增加的。

b)Hashtable 是同步的,比较慢,但 HashMap 没有同步策略,所以会更快。

c)Hashtable 不允许有个空的 key,但是 HashMap 允许出现一个 null key。

更多的不同之处参见答案。

 

[第48题] Java 中的 HashSet,内部是如何工作的?

 

HashSet 的内部采用 HashMap来实现。由于 Map 需要 key 和 value,所以所有 key 的都有一个默认 value。类似于 HashMap,HashSet 不允许重复的 key,只允许有一个null key,意思就是 HashSet 中只允许存储一个 null 对象。

 

[第49题] 写一段代码在遍历 ArrayList 时移除一个元素?

 

该问题的关键在于面试者使用的是 ArrayList 的 remove() 还是 Iterator 的 remove()方法。这有一段示例代码,是使用正确的方式来实现在遍历的过程中移除元素,而不会出现 ConcurrentModificationException 异常的示例代码。

 


[第50题] 我们能自己写一个容器类,然后使用 for-each 循环码?

 

可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。

 

[第51题] ArrayList 和 HashMap 的默认大小是多数?

 

在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。这就是 Java 7 中 ArrayList 和 HashMap 类的代码片段:

// from ArrayList.java JDK 1.7
private static final int DEFAULT_CAPACITY = 10;
//from HashMap.java JDK 7
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

[第52题] 集合框架中的泛型有什么优点?

Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。


[第53题] 为何Collection不从Cloneable和Serializable接口继承?

Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。
当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。
在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。

[第54题] 为何Map接口不继承Collection接口?

尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。
如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。

 

[第四部分] 流


[第55题] 给定一个txt文件,如何得到某字符串出现的次数


File file = new File("E://test.txt");

InputStream is = new FileInputStream(file);

byte b[] = new byte[1024];

int a = is.read(b);

String str[] = new String(b,0,a).split("");

 


int count = 0;

for(int i = 0;i<str.length;i++){

if("a".equals(str[i]))count++;

}

System.out.println(count);

 

 

[第56题] 讲讲IO里面的常见类,字节流,字符流、接口、实现类、方法阻塞

答:输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。我们用Eclipse开发小程序在控制台输入数据就属于输入流,即从控制台输入到内存。

IO里面常见的类,第一印象就只知道IO流中有很多类。
从流的传输单位区分: IO流主要分为字符流和字节流。字符流中有抽象类InputStream和OutputStream,它们的子类FileInputStream,FileOutputStream,BufferedOutputStrea等。字符流BufferedReader和Writer等。都实现了Closeable, Flushable, Appendable这些接口。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。

java中的阻塞式方法是指在程序调用改方法时,必须等待输入数据可用或者检测到输入结束或者抛出异常,否则程序会一直停留在该语句上,不会执行下面的语句。比如read()和readLine()方法。


Java IO体系中有太多的对象,到底用哪个呢?
明确操作的数据设备。
数据source对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据destination对应的设备:硬盘(File),内存(数组),控制台(System.out)。

[第57题]  什么是流,按照传输单位,分成哪两种流,并且他们的父类叫什么流是指数据的传输

 

字节流,字符流
字节流:InputStream OutputStream
字符流:Reader Writer


[第58题]   什么是节点流,什么是处理流,它们各有什么用处,处理流的创建有什么特征

 

节点流 直接与数据源相连,用于输入或者输出
处理流:在节点流的基础上对之进行加工,进行一些功能的扩展
处理流的构造器必须要 传入节点流的子类
[第59题] String编码UTF-8和GBK的区别

答:UTF-8是国际通用的编码,通用性比较好,GBK是国家编码,支持中文,但是相比UTF-8的通用性差,不过UTF-8占用的数据库比较大,所以相对反应速度要慢。


[第60题] 什么时候使用字节流,什么时候    使用字符流

答:所有的输入都是转换成字节流之后,然后在内存中变成字符流。所以一般建议使用字符流。但是遇到中文汉字,出现乱码的情况下,可以使用字节流。

在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。我们建议尽量尝试使用字符流,一旦程序无法成功编译,就不得不使用面向字节的类库,即字节流。


[第61题] 递归读取文件夹的文件,代码怎么实现

答:利用File中的listFiles方法,将所有文件保存访问,然后递归调用。代码如下:

publicclass Test {

  publicvoidvisitFile(String path){

     if(path==null){

       return;//因为下面的new File如果path为空,回报异常

     }

     File[] files=new File(path).listFiles();

     if(files==null){

       return;

     }

     for(File file:files){

       if(file.isFile()){

         System.out.println(file.getName());

       }elseif(file.isDirectory()){

         System.out.println("Directory");

         visitFile(file.getPath());

       }else{

         System.out.println("Error");

       }

     }

   

  }

  public static void main(String[]args) {

     Test dc = new Test();

     dc.visitFile("F:/文件");

  }

}

[第62题]  什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别

 

Bit最小的二进制单位 ,是计算机的操作部分 取值0或者1
Byte是计算机操作数据的最小单位由8位bit组成 取值(-128-127)
Char是用户的可读写的最小单位,在java里面由16位bit组成 取值(0-65535)

Bit 是最小单位 计算机 只能认识 0或者1

8个字节 是给计算机看的
字符 是看到的东西  一个字符=二个字节

 

[第63题] BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法

 

属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法


[第64题]   如果我要对字节流进行大量的从硬盘读取,要用那个流,为什么

 

BufferedInputStream 使用缓冲流能够减少对硬盘的损伤
Java设计模式思想

[第65题]   如果我要打印出不同类型的数据到数据源,那么最适合的流是那个流,为什么

 

Printwriter 可以打印各种数据类型

[第66题]   怎么样把我们控制台的输出改成输出到一个文件里面,这个技术叫什么

 

SetOut(printWriter,printStream)重定向


[第67题] 怎么样把输出字节流转换成输出字符流,说出它的步骤

 

使用 转换处理流OutputStreamWriter 可以将字符流转为字节流
New OutputStreamWriter(new FileOutputStream(File file));


[第68题]   把包括基本类型在内的数据和字符串按顺序输出到数据源,或者按照顺序从数据源读入,一般用哪两个流

 

DataInputStream DataOutputStream

[第69题]   把一个对象写入数据源或者从一个数据源读出来,用哪两个流

 

ObjectInputStream ObjectOutputStream

 

[第70题] 什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作

 

对象序列化,将对象以二进制的形式保存在硬盘上
反序列化;将二进制的文件转化为对象读取
实现serializable接口

不想让字段放在硬盘上就加transient


[第71题] 在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用

 

是版本号,要保持版本号的一致 来进行序列化

为了防止序列化出错


[第72题]   OutputStream里面的write()是什么意思,write(byte b[], int off, int len)这个方法里面的三个参数分别是什么意思

 

write将指定字节传入数据源
Byte b[ ]是byte数组
b[off]是传入的第一个字符
b[off+len-1]是传入的最后的一个字符
len是实际长度

[第73题]  IO流一般需要不需要关闭,如果关闭的话在用什么方法,一般要在那个代码块里面关闭比较好,处理流是怎么关闭的,如果有多个流互相调用传入是怎么关闭的?

答案

流一旦打开就必须关闭,使用close方法
放入finally语句块中(finally 语句一定会执行)
调用的处理流就关闭处理流
多个流互相调用只关闭最外层的流


[第74题] 写一段代码读取一个序列化的对象一般使用哪种Stream?

 

A、InputStream B、FileReader C、DataInputStream D、ObjectStream

 

[第75题]  io流怎样读取文件的?

 

使用File对象获取文件路径,通过字符流Reader加入文件,使用字符缓存流BufferedReader处理Reader,再定义一个字符串,循环遍历出文件。代码如下:
File file = new File("d:/spring.txt");
try {
Reader reader = new FileReader(file);
BufferedReader buffered = new BufferedReader(reader);
String data = null;
while((data = buffered.readLine())!=null){
System.out.println(data);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

[第76题] FileInputStream 创建详情,就是怎样的创建不报错,它列出了几种形式!

 

FileInputStream是InputStream的子类,通过接口定义,子类实现创建FileInputStream,


[第77题] 用io流中的技术,指定一个文件夹的目录,获取此目录下的所有子文件夹路径

[第78题] 请问你在什么情况下会在你得java代码中使用可序列化? 如何实现java序列化?

 

把一个对象写入数据源或者从一个数据源读出来,使用可序列化,需要实现Serializable接口

 

[第79题] 问:简单说说你平时使用的 Java IO 流中涉及到了哪些设计策略和设计模式?

答:首先 Java 的 IO 库提供了一种链接(Chaining)机制,可以将一个流处理器跟另一个流处理器首尾相接,以其中之一的输出作为另一个的输入而形成一个流管道链接,譬如常见的 new DataInputStream(new FileInputStream(file)) 就是把 FileInputStream 流当作 DataInputStream 流的管道链接。
其次,对于 Java IO 流还涉及一种对称性的设计策略,其表现为输入输出对称性(如 InputStream 和 OutputStream 的字节输入输出操作,Reader 和 Writer 的字符输入输出操作)和字节字符的对称性(InputStream 和 Reader 的字节字符输入操作,OutputStream 和 Writer 的字节字符输出操作)。
此外,对于 Java IO 流在整体设计上还涉及装饰者(Decorator)和适配器(Adapter)两种设计模式。
对于 IO 流涉及的装饰者设计模式例子如下:
//把InputStreamReader装饰成BufferedReader来成为具备缓冲能力的Reader。
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

对于 IO 流涉及的适配器设计模式例子如下:
//把FileInputStream文件字节流适配成InputStreamReader字符流来操作文件字符串。
FileInputStream fileInput = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

而对于上面涉及的两种设计模式通俗总结如下。
装饰者模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各种字符流间装饰,各种字节流间装饰)。
适配器模式就是将某个类的接口转换成我们期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题(字符流与字节流间互相适配)。

[第80题] 问:什么是缓冲区?有什么作用?

答:缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁地操作一个资源(如文件或数据库)则性能会很低,所以为了提升性能就可以将一部分数据暂时读写到缓存区,以后直接从此区域中读写数据即可,这样就显著提升了性能。
对于 Java 字符流的操作都是在缓冲区操作的,所以如果我们想在字符流操作中主动将缓冲区刷新到文件则可以使用 flush() 方法操作。

[第81题] 问:字节流和字符流哪个好?怎么选择?

答:缓大多数情况下使用字节流会更好,因为字节流是字符流的包装,而大多数时候 IO 操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的(图片等都是按字节存储的)。
而如果对于操作需要通过 IO 在内存中频繁处理字符串的情况使用字符流会好些,因为字符流具备缓冲区,提高了性能。


[第82题] 说说对IO流的理解


IO分为File IO和Socket IO,File IO基本上是不会问的,问也问不出什么来,平时会用就好了,另外记得File IO都是阻塞IO。

Socket IO是比较重要的一块,要搞懂的是阻塞/非阻塞的区别、同步/异步的区别,借此理解阻塞IO、非阻塞IO、多路复用IO、异步IO这四种IO模型,Socket IO如何和这四种模型相关联。这是基本一些的,深入一些的话,就会问NIO的原理、NIO属于哪种IO模型、NIO的三大组成等等,这有些难,当时我也是研究了很久才搞懂NIO。提一句,NIO并不是严格意义上的非阻塞IO而应该属于多路复用IO,面试回答的时候要注意这个细节,讲到NIO会阻塞在Selector的select方法上会增加面试官对你的好感。

如果用过Netty,可能会问一些Netty的东西,毕竟这个框架基本属于当前最好的NIO框架了(Mina其实也不错,不过总体来说还是比不上Netty的),大多数互联网公司也都在用Netty。

 


[第五部分] 设计模式


[第七篇] 单例模式

单例模式核心只需要new一个实例对象的模式,比如数据库连接,在线人数等,一些网站上看到的在线人数统计就是通过单例模式实现的,把一个计时器存放在数据库或者内存中,当有人登陆的时候取出来加一再放回去,有人退出登陆的时候取出来减一再放回去,但是当有两个人同时登陆的时候,会同时取出计数器,同时加一,同时放回去,这样的话数据就会错误,所以需要一个全局变量的对象给全部人使用,只需要new出一个实例对象,这就是单例模式的应用,并且单例模式节省资源,因为它控制了实例对象的个数,并有利于gc回收。

[第1题] 手写单例模式(饿汉和饱汉模式)和工厂模式?

(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();}}


懒汉模式

public class Singleton {

    private static Singleton instance = null;
    private Singleton(){}

    public static synchronized Singleton getInstance(){
        //如果还没有被实例化过,就实例化一个,然后返回
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
  饿汉模式

public class Singleton {
    //类加载的时候instance就已经指向了一个实例
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}
  双重检验锁

public class Singleton {
   
    private static Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


 静态内部类:因为JAVA静态内部类的特性,加载的时候不会加载内部静态类,使用的时候才会加载,而使用的时候类加载又是线程安全的,这就完美达到了效果;

public class Singleton {

    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }

    private Singleton(){}

    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

 

 

[第2题] 策略模式

就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强。比如有个需求是写鸭子对象,鸭子有叫,飞,外形这三种方法,如果每个鸭子类都写这三个方法会出现代码的冗余,这时候我们可以把鸭子中的叫,飞,外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强,当用户提出新的需求比如鸭子会游泳,那么对于我们oo程序员来讲就非常简单了我们只需要在鸭父类中加一个游泳的方法,让会游泳的鸭子重写游泳方法就可以了。


[第3题]  工厂模式

 

简单的工厂模式主要是统一提供实例对象的引用,通过工厂模式接口获取实例对象的引用。比如一个登陆功能,后端有三个类,controller类,interface类,实现接口的实现类。当客户端发出一个请求,当请求传到controller类中时,controller获取接口的引用对象,而实现接口的实现类中封装好了登陆的业务逻辑代码。当你需要加一个注册需求的时候只需要在接口类中加一个注册方法,实现类中实现方法,controller获取接口的引用对象即可,不需要改动原来的代码,这种做法是的可拓展性强。


[第4题] AOP与IOC的概念(即spring的核心)

a) IOC:Spring是开源框架,使用框架可以使我们减少工作量,提高工作效率并且它是分层结构,即相对应的层处理对应的业务逻辑,减少代码的耦合度。而spring的核心是IOC控制反转和AOP面向切面编程。IOC控制反转主要强调的是程序之间的关系是由容器控制的,容器控制对象,控制了对外部资源的获取。而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,而在IOC中是容器帮我们创建对象并注入依赖对象,正是容器帮我们查找和注入对象,对象是被获取,所以叫反转。

b) AOP:面向切面编程,主要是管理系统层的业务,比如日志,权限,事物等。AOP是将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为切面(aspect),切面将那些与业务逻辑无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

 

25 用什么把对象动态的写入磁盘中,写入要实现什么接口。

 

ObjectInputStream,需要实现Serializable接口


[第六部分] 算法

排序和查找需要分开看待。
[第5题] 冒泡排序


[第6题] 二分查找

 

 

[第7题]  数据结构必知必会
数据结构和算法分析,对于一名程序员来说,会比不会好而且在工作中绝对能派上用场。数组、链表是基础,栈和队列深入一些但也不难,树挺重要的,比较重要的树AVL树、红黑树,可以不了解它们的具体实现,但是要知道什么是二叉查找树、什么是平衡树,AVL树和红黑树的区别。记得某次面试,某个面试官和我聊到了数据库的索引,他问我:

[第8题] 你知道索引使用的是哪种数据结构实现吗?

答到用的Hash表吧,答错。他又问,你知道为什么要使用树吗?答到因为Hash表可能会出现比较多的冲突,在千万甚至是上亿级别的数据面前,会大大增加查找的时间复杂度。而树比较稳定,基本保证最多二三十次就能找到想要的数据,对方说不完全对,最后我们还是交流了一下这个问题,我也明白了为什么要使用树,这里不说,网友朋友们觉得索引为什么要使用树来实现呢?

 

 

 

 


[第七部分] 多线程

[第9题] 说说对多线程的理解
a)一个进程是一个独立的运行环境,可以看做是一个程序,而线程可以看做是进程的一个任务,比如QQ是一个进程,而一个QQ窗口是一个线程。

b)在多线程程序中,多线程并发可以提高程序的效率,cpu不会因为某个线程等待资源而进入空闲状态,它会把资源让给其他的线程。

c)用户线程就是我们开发程序是创建的线程,而守护线程为系统线程,如JVM虚拟中的GC

d)线程的优先级别:每一个线程都有优先级别,有限级别高的可以先获取CPU资源使该线程从就绪状态转为运行状态。也可以自定义线程的有限级别

e)死锁:至少两个以上线程争取两个以上cpu资源,避免死锁就避免使用嵌套锁,只需要在他们需要同步的地方加锁和避免无限等待

 

[第10题] 进程和线程的区别是什么?
进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。
[第11题] 创建线程有几种不同的方式?你喜欢哪一种?为什么?
有三种方式可以用来创建线程:
继承Thread类
实现Runnable接口
应用程序可以使用Executor框架来创建线程池
实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。

使用ExecutorService、Callable、Future实现有返回结果的多线程

 

[第12题] 概括的解释下线程的几种可用状态。
线程在执行过程中,可以处于下面几种状态:
就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。
[第13题] 同步方法和同步代码块的区别是什么?
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。

[第14题] synchronized关键字的用法,优缺点

  答:java关键字,当它用来修饰一个方法或者代码块的时候,能够保证在同一时刻最多只有一个线程执行该代码段的代码;

    synchronized修饰的方法或者对象,只能以同步的方式执行,会引起性能问题;无法中断一个正在等候获得锁的线程,也无法通过投票获得锁;一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险;
[第15题] Lock接口有哪些实现类,使用场景是什么

  答:Lock接口有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLock和WriteLock。

  使用场景:一般应用于多度少写,因为读的线程之间没有竞争,所以比起synchronzied,性能要好很多;

[第16题] ABC三个线程如何保证顺序执行

  答:用Thread.join() 方法,或者线程池newSingleThreadExecutor(原理是会将所有线程放入一个队列,而队列则保证了FIFO),也可以通过ReentrantLock,state整数用阿里判断轮到谁来执行
[第17题] 在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。


[第18题] 悲观锁、乐观锁的优缺点,CAS有什么缺陷,该如何解决

  悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁,这样别人拿数据的时候就会阻塞知道它拿到锁;比如关系型数据库的行锁、表锁、读锁、写锁;比如java里面的同步原语synchronized关键字的实现也是悲观锁;

  乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再次期间别人有没有更新这个数据。乐观锁适用于多读的应用类型,可以提高吞吐量。java中java.util.conncurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的;

  CAS:CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程不会被挂起,而是被告知这次竞争失败,并可以再次尝试;

  CAS的缺陷:ABA问题、循环时间长开销大,只能保证一个共享变量的原子操作;


[第19题] ABC三个线程如何保证顺序执行

  答:用Thread.join() 方法,或者线程池newSingleThreadExecutor(原理是会将所有线程放入一个队列,而队列则保证了FIFO),也可以通过ReentrantLock,state整数用阿里判断轮到谁来执行
[第20题] 假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
[第21题] 线程池的问题

另外,线程池也是比较常问的一块,常用的线程池有几种?这几种线程池之间有什么区别和联系?线程池的实现原理是怎么样的?实际一些的,会给你一些具体的场景,让你回答这种场景该使用什么样的线程池比较合适。

 

[第22题] 线程的状态都有哪些(五大状态)

  新建状态(new):当用new操作符创建一个线程时,如new Thread(),线程还没有开始运行,此时处于仙剑状态;

  就绪状态(runnable):一个新创建的线程并不自动开始运行,要执行线程,必须要调用线程的start()方法,当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态;

  运行状态(running):当线程获得cpu时间后,他才进入运行状态,真正开始实行run()方法

  阻塞状态(blocked):当线程运行过程中,可能由于各种原因进入阻塞状态;

    a.线程通过调用sleep方法进入睡眠状态

    b.线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者

    c.线程试图得到一个锁,而该锁正被其他线程持有

    d.线程正等待某个触发条件

  死亡状态(dead):run方法自然退出而自然死亡,或者一个未捕获的异常终止了run方法而使线程猝死

  

 
[第23题] 什么是死锁(deadlock)?
两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。
[第24题] 如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
[第25题] 什么是线程局部变量?
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java 提供 ThreadLocal 类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

[第26题] 用 wait-notify 写一段代码来解决生产者-消费者问题?

请参考答案中的示例代码。只要记住在同步块中调用 wait() 和 notify()方法,如果阻塞,通过循环来测试等待条件。

【生产者】

package com.edu.chapter03.test; 
import java.util.Vector; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
 
public class Producer implements Runnable { 
 
    private final Vector sharedQueue; 
    private final int SIZE; 
     
    public Producer(Vector sharedQueue, int size) { 
        this.sharedQueue = sharedQueue; 
        this.SIZE = size; 
    } 
     
   @Override 
    public void run() { 
        // TODO Auto-generated method stub 
        for (int i = 0; i < 7; i++) { 
            System.out.println("Produced:" + i); 
            try { 
                produce(i); 
            } catch (InterruptedException ex) { 
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex); 
            } 
        } 
    } 
 
    private void produce(int i) throws InterruptedException { 
         
        //wait if queue is full 
        while (sharedQueue.size() == SIZE) { 
            synchronized (sharedQueue) { 
                System.out.println("Queue is full " + Thread.currentThread().getName() 
                        + " is waiting , size: " + sharedQueue.size()); 
                sharedQueue.wait(); 
            } 
        } 
         
        //producing element and notify consumers 
        synchronized (sharedQueue) { 
            sharedQueue.add(i); 
            sharedQueue.notifyAll(); 
        } 
    } 

【消费者】

package com.edu.chapter03.test; 
import java.util.Vector; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
 
public class Consumer implements Runnable { 
 
    private final Vector sharedQueue; 
    private final int SIZE; 
     
    public Consumer(Vector sharedQueue, int size) { 
        this.sharedQueue = sharedQueue; 
        this.SIZE = size; 
    } 
 
    @Override 
    public void run() { 
        // TODO Auto-generated method stub 
        while (true) { 
            try { 
                System.out.println("Consumer: " + consume()); 
                Thread.sleep(50); 
            } catch (InterruptedException ex) { 
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex); 
            } 
        } 
    } 
     
    private int consume() throws InterruptedException { 
         
        //wait if queue is empty 
        while (sharedQueue.isEmpty()) { 
            synchronized (sharedQueue) { 
                System.out.println("Queue is empty " + Thread.currentThread().getName() 
                        + " is waiting , size: " + sharedQueue.size()); 
                sharedQueue.wait(); 
            } 
        } 
         
        //otherwise consume element and notify waiting producer 
        synchronized (sharedQueue) { 
            sharedQueue.notifyAll(); 
            return (Integer) sharedQueue.remove(0); 
        } 
    } 

【测试函数】

package com.edu.chapter03.test; 
import java.util.Vector; 
 
public class ProducerConsumerSolution { 
 
    public static void main(String[] args) { 
        Vector sharedQueue = new Vector(); 
        int size = 4; 
        Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer"); 
        Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer"); 
        prodThread.start(); 
        consThread.start(); 
    } 
}

 

[第27题] 用 Java 写一个线程安全的单例模式(Singleton)?

 

请参考答案中的示例代码,这里面一步一步教你创建一个线程安全的 Java 单例类。当我们说线程安全时,意思是即使初始化是在多线程环境中,仍然能保证单个实例。Java 中,使用枚举作为单例类是最简单的方式来创建线程安全单例模式的方式。

 

立即加载/饿汉式:

【在调用方法前,实例就已经被创建】

package com.weishiyao.learn.day8.singleton.ep1;

public class MyObject {
  // 立即加载方式==恶汉模式
  private static MyObject myObject = new MyObject();

  private MyObject() {
  }
 
  public static MyObject getInstance() {
    // 此代码版本为立即加载
    // 此版本代码的缺点是不能有其他实例变量
    // 因为getInstance()方法没有同步
    // 所以有可能出现非线程安全的问题
    return myObject;
  }
}
【创建线程类】

package com.weishiyao.learn.day8.singleton.ep1;

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}
【创建运行类】
package com.weishiyao.learn.day8.singleton.ep1;

public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
  }
}
延迟加载/懒汉式

【建对象的实例】

package com.weishiyao.learn.day8.singleton.ep2;

public class MyObject {
  private static MyObject myObject;
 
  private MyObject() {
   
  }
 
  public static MyObject getInstance() {
    // 延迟加载
    if (myObject != null) {
     
    } else {
      myObject = new MyObject();
    }
    return myObject;
  }
}
【创建线程类】

package com.weishiyao.learn.day8.singleton.ep2;

public class MyThread extends Thread {
  @Override
  public void run() {
    System.out.println(MyObject.getInstance().hashCode());
  }
}
【创建运行类】

package com.weishiyao.learn.day8.singleton.ep2;

public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    t1.start();
  }
}
【运行测试类】

package com.weishiyao.learn.day8.singleton.ep2;

public class Run {
  public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();
    MyThread t3 = new MyThread();
    MyThread t4 = new MyThread();
    MyThread t5 = new MyThread();
    t1.start();
    t2.start();
    t3.start();
    t4.start();
    t5.start();
  }
}


[第28题] Java 中 sleep 方法和 wait 方法的区别?

 

虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。

答:首先,sleep()方法属于Thread类的,而wait()方法是属于Object类的;sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持,当指定的时间到了又自动回恢复运行状态,调用了sleep()方法的过程中,线程不会释放对象锁;而当调用了wait()方法的时候,线程回放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备。


[第29题] notify()和notifyAll()的区别

  答:notify()方法表示,当前线程已经放弃对资源的占有,通知等待的线程来获取对资源的占有权,但是只有一个线程能够从wait状态中恢复;notifyAll()方法表示,当前的线程已经放弃对资源的占有,通知所有的等待线程从wait()方法后的语句开始执行,但最终只有一个线程能竞争获得锁并执行;notify()是对notifyAll()的一个优化,
[第30题] ThreadLocal的了解,实现原理。

ThreadLocal,线程本地变量。定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写都是线程隔离的,互相之间不会影响,他提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制;实现的思路,Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals,也就是说每个线程都有一个自己的ThreadLocalMap。ThreadLocalMap有自己的独立实现,可以简单的将它的key视作ThreadLocal,value为代码中放入的值(实际上key并不是ThreadLocal本省,而是它的一个弱引用)。每个线程在往ThreadLocal里set值的时候,都会往自己的ThreadLocalMap里存,读也是已某个ThreadLocal作为引用,在自己的map里找对应的key,从而实现了线程的隔离。


[第31题] Java 中 ++ 操作符是线程安全的吗?

不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存储回内存,这个过程可能会出现多个线程交差。


[第八部分] 操作系统

[第32题] 简述你所知道的Linux:

Linux起源于1991年,1995年流行起来的免费操作系统,目前, Linux是主流的服务器操作系统, 广泛应用于互联网、云计算、智能手机(Android)等领域。由于Java主要用于服务器端的开发,因此Java应用的部署环境有很多为Linux。
Windows操作系统的目录结构,是以盘符为单位,C盘、D盘、E盘等等,数据存储在各个盘符之下,而Linux操作系统最顶层只有一个根目录root,所有文件都存储在这一个根目录之下。
Linux不像Windows的图形操作界面,是通过命令的方式进行操作,常用命令有:
a . pwd:用于显示当前工作目录;
b . ls:用于查看当前工作目录内容;
c . cd:用于改变当前工作目录。


[第九部分] web

[第33题] servlet执行流程

客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request、response对象传递给找到的servlet,servlet根据request就可以知道是谁发出的请求,请求信息及其他信息,当servlet处理完业务逻辑后会将信息放入到response并响应到客户端。

[第34题] springMVC的执行流程

springMVC是由dispatchservlet为核心的分层控制框架。首先客户端发出一个请求web服务器解析请求url并去匹配dispatchservlet的映射url,如果匹配上就将这个请求放入到dispatchservlet,dispatchservlet根据mapping映射配置去寻找相对应的handel,然后把处理权交给找到的handel,handel封装了处理业务逻辑的代码,当handel处理完后会返回一个逻辑视图modelandview给dispatchservlet,此时的modelandview是一个逻辑视图不是一个正式视图,所以dispatchservlet会通过viewresource视图资源去解析modelandview,然后将解析后的参数放到view中返回到客户端并展现。


[第35题] Struts工作流程

a) 客户端发出一个请求到servlet容器

b) 请求经过一些列过滤被filterdispatcher调用,filterdispatch通过actionMapper去找相对应的action。

c) Actionmapper找到对应的action返回给filterdispatch,dispatch把处理权交给actionproxy

d) Actionproxy通过配置文件找到对应的action类

e) Actionproxy创建一个actionIinvocation的实例处理业务逻辑

[第36题] 对ajax的理解

Ajax为异步请求,即局部刷新技术,在传统的页面中,用户需要点击按钮或者事件触发请求,到刷新页面,而异步技术为不需要点击即可触发事件,这样使得用户体验感增强,比如商城购物车的异步加载,当你点击商品时无需请求后台而直接动态修改参数。

 

[第37题] Tomcat服务器优化

a) 内存优化:主要是对Tomcat启动参数进行优化,我们可以在Tomcat启动脚本中修改它的最大内存数等等。

b) 线程数优化:Tomcat的并发连接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理性能等等。

c) 优化缓存:打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当的修改。

tomcat的压缩优化就是将返回的html页面等内容经过压缩,压缩成gzip格式之后,发送给浏览器,浏览器在本地解压缩的过程。

[第38题] HTTP协议

a) 常用的请求方法有get、post

b) Get与post的区别:传送数据,get携带参数与访问地址传送,用户可以看见,这的话信息会不安全,导致信息泄露。而post则将字段与对应值封装在实体中传送,这个过程用户是不可见的。Get传递参数有限制,而post无限制。

[第39题] TCP/UDP协议


[第40题] Spring使用了哪些设计模式

  (1)工厂模式,在各种BeanFactory以及ApplicationContext创建中都用到了;

  (2)模板模式,也是在各种BeanFactory以及ApplicationContext创建中都用到了;

  (3)代理模式,在AOP实现中用到了JDK的动态代理;

  (4)单例模式,比如创建bean的时候;

  (5)策略模式,第一个地方,加载资源文件的地方,使用了不同的方法,比如:classPathResource,FileSystemResource,ServletContextResource,UrlResource但他们都有共同的接口Resource;第二个地方就是AOP的实现中,采用了不同的方式,JDK动态代理和CGLIB代理;

 

[第41题] session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。


[第42题] struts1和struts2的区别

[第43题] struts2和springMVC的区别

[第44题] spring框架中需要引用哪些jar包,以及这些jar包的用途

[第45题] srpingMVC的原理

[第46题] springMVC注解的意思

[第47题] spring中beanFactory和ApplicationContext的联系和区别

[第48题] spring注入的几种方式

[第49题] spring如何实现事物管理的

[第50题] springIOC和AOP的原理

[第51题] hibernate中的1级和2级缓存的使用方式以及区别原理

[第52题] spring中循环注入的方式


[第十部分] 数据库

[第53题] 高并发处理

a) 了解一点高并发性问题,比如一W人抢一张票时,如何保证票在没买走的情况下所有人都能看见这张票,显然是不能用同步机制,因为synchronize是锁同步一次只能一个人进行。这时候可以用到锁机制,采用乐观锁可以解决这个问题。乐观锁的简单意思是在不锁定表的情况下,利用业务的控制来解决并发问题,这样即保证数据的可读性,又保证保存数据的排他性,保证性能的同时解决了并发带来的脏读数据问题。

 

[第54题] 事物的理解

a) 事物具有原子性,一致性,持久性,隔离性

b) 原子性:是指在一个事物中,要么全部执行成功,要么全部失败回滚。

c) 一致性:事物执行之前和执行之后都处于一致性状态

d) 持久性:事物多数据的操作是永久性

e) 隔离性:当一个事物正在对数据进行操作时,另一个事物不可以对数据进行操作,也就是多个并发事物之间相互隔离。

 

[第55题] 索引的分类

众所周知,索引是关系型数据库中给数据库表中一列或多列的值排序后的存储结构,SQL的主流索引结构有B+树以及Hash结构,聚集索引以及非聚集索引用的是B+树索引。这篇文章会总结SQL Server以及MySQL的InnoDB和MyISAM两种SQL的索引。

SQL Sever索引类型有:唯一索引,主键索引,聚集索引,非聚集索引。

MySQL 索引类型有:唯一索引,主键(聚集)索引,非聚集索引,全文索引。

聚集(clustered)索引定义:
数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。,也叫聚簇索引。

创建表的时候指定主键(注意:SQL Sever默认主键为聚集索引,也可以指定为非聚集索引,而MySQL里主键就是聚集索引);如果不创建索引,系统会自动创建一个隐含列作为表的聚集索引。

值得注意的是,最好还是在创建表的时候添加聚集索引,由于聚集索引的物理顺序上的特殊性,因此如果再在上面创建索引的时候会根据索引列的排序移动全部数据行上面的顺序,会非常地耗费时间以及性能。

 

非聚集(unclustered)索引定义:
该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。
其实按照定义,除了聚集索引以外的索引都是非聚集索引,只是人们想细分一下非聚集索引,分成普通索引,唯一索引,全文索引。如果非要把非聚集索引类比成现实生活中的东西,那么非聚集索引就像新华字典的偏旁字典,他结构顺序与实际存放顺序不一定一致。

非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据。

 

[第56题] 存储引擎的 InnoDB 与 MyISAM

1)InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。

2)MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用

3)InnoDB支持外键,MyISAM不支持

4)从MySQL5.5.5以后,InnoDB是默认引擎

5)InnoDB不支持FULLTEXT类型的索引

6)InnoDB中不保存表的行数,如select count() from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含where条件时MyISAM也需要扫描整个表

7)对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引

8)清空整个表时,InnoDB是一行一行的删除,效率非常慢。MyISAM则会重建表

9)InnoDB支持行锁(某些情况下还是锁整表,如 update table set a=1 where user like ‘%lee%’


[第57题] 常见的数据库优化手段

  答:库表优化,表设计合理化,符合三大范式;添加适当的索引(普通索引、主键索引、唯一索引、全文索引);分库分表;读写分离等;sql语句优化,定位执行效率低,慢sql的语句,通过explain分析低效率的原因;

 

[第58题] 索引的优缺点,什么字段上建立索引

  答:优点方面:第一,通过创建唯一索引可以保证数据的唯一性;第二,可以大大加快数据的检索速度,是主要目的;第三;在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间;第四,可以在查询中使用优化隐藏器,提高系统的性能;

  缺点方面:第一,创建索引和维护索引要耗费时间,并且随着数据量的增加而增加;第二,每一个索引需要占用额外的物理空间,需要的磁盘开销更大;第三,当对表中的数据进行增加、删除、修改操作时,索引也要动态维护,降低了数据的维护速度;

  一般来说,在经常需要搜索的列上,强制该列的唯一性和组织表中数据的排列结构的列,在经常用在链接的列上,在经常需要排序的列上,在经常使用在where字句的列上可以添加索引,以提升查询速度;同样,对于一些甚少使用或者参考的列,只有很少数值的列(如性别),定义为text,image,bit的列,修改性能远远大于检索性能的列不适合添加索引;

 

[第59题] 数据库连接池

  答:数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态的对池中的连接进行申请、使用、释放;

  (1)程序初始化时创建连接池

  (2)使用时向连接池申请可用连接

  (3)使用完毕,将连接返还给连接池

  (4)程序退出时,断开所有的连接,并释放资源
[第60题] 避重就轻


数据库十有八九也都会问到。一些基本的像union和union all的区别、left join、几种索引及其区别就不谈了,比较重要的就是数据库性能的优化,如果对于数据库的性能优化一窍不通,那么有时间,还是建议你在面试前花一两天专门把SQL基础和SQL优化的内容准备一下。

不过数据库倒是不用担心,一家公司往往有很多部门,如果你对数据库不熟悉而基本技术又非常好,九成都是会要你的,估计会先把你放到对数据库使用不是要求非常高的部门锻炼一下。

 

 

 

 

[第十一部分] end