AAA最新Java面试题整理.docx
《AAA最新Java面试题整理.docx》由会员分享,可在线阅读,更多相关《AAA最新Java面试题整理.docx(53页珍藏版)》请在冰豆网上搜索。
AAA最新Java面试题整理
1基础篇
1.1基本功
1.1.1面向对象特征
封装,继承,多态和抽象
1、封装
封装给对象提供了隐藏内部特性和行为的能力。
对象提供一些能被其他对象访问的方法来改变它内部的数据。
在Java当中,有3种修饰符:
public,private和protected。
每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。
下面列出了使用封装的一些好处:
1)通过隐藏对象的属性来保护对象内部的状态。
2)提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。
3)禁止对象之间的不良交互提高模块化
2、继承
继承给对象提供了从基类获取字段和方法的能力。
继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。
3、多态
多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。
一个多态类型上的操作可以应用到其他类型的值上面。
4、抽象
抽象是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细节来创建类。
Java支持创建只暴漏接口而不包含方法实现的抽象的类。
这种抽象技术的主要目的是把类的行为和实现细节分离开。
1.1.2final,finallR,finalize的区别
1、final修饰符(关键字)
如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。
因此一个类不能既被声明为abstract的,又被声明为final的。
将变量或方法声明为final,可以保证它们在使用中不被改变。
被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
被声明为final的方法也同样只能使用,不能重载。
2、finallR
在异常处理时提供finallR块来执行任何清除操作。
如果抛出一个异常,那么相匹配的catch子句就会执行,然后控制就会进入finallR块(如果有的话)。
3、finalize
方法名。
Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
它是在Object类中定义的,因此所有的类都继承了它。
子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。
finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
1.1.3int和Integer有什么区别
int是基本数据类型,而Integer是其包装类,注意是一个类。
为什么要提供包装类呢?
?
?
一是为了在各种类型间转化,通过各种方法的调用。
否则你无法直接通过变量转化。
1.1.4重载和重写的区别
override(重写)
1.方法名、参数、返回值相同。
2.子类方法不能缩小父类方法的访问权限。
3.子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
4.存在于父类和子类之间。
5.方法被定义为final不能被重写。
overload(重载)
1.参数类型、个数、顺序至少有一个不相同。
2.不能重载只有返回值不同的方法名。
3.存在于父类和子类、同类中。
区别点
重载
重写(覆写)
英文
Overloading
Overiding
定义
方法名称相同,参数的类型或个数不同
方法名称、参数类型、返回值类型全部相同
权限
对权限没要求
被重写的方法不能拥有更严格的权限
范围
发生在一个类中
发生在继承类中
1.1.5抽象类和接口有什么区别
接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法,一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。
还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用。
1.1.6说说反射的用途及实现
Java反射机制主要提供了以下功能:
在运行时构造一个类的对象;判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。
反射最大的应用就是框架
Java反射的主要功能:
-确定一个对象的类
-取出类的modifiers,数据成员,方法,构造器,和超类.
-找出某个接口里定义的常量和方法说明.
-创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).
-取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.
-在运行时刻调用动态对象的方法.
-创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.
反射的应用很多,很多框架都有用到
spring的ioc/di也是反射….
javaBean和jsp之间调用也是反射….
struts的FormBean和页面之间…也是通过反射调用….
JDBC的classForName()也是反射…..
hibernate的find(Classclazz)也是反射….
反射还有一个不得不说的问题,就是性能问题,大量使用反射系统性能大打折扣。
怎么使用使你的系统达到最优就看你系统架构和综合使用问题啦,这里就不多说了。
来源:
http:
//uule.iteR
1.1.7说说自定义注解的场景及实现
登陆、权限拦截、日志处理,以及各种Java框架,如Spring,Hibernate,JUnit提到注解就不能不说反射,Java自定义注解是通过运行时靠反射获取注解。
实际开发中,例如我们要获取某个方法的调用日志,可以通过AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志注解,就进行日志记录。
1.1.8HTTP请求的GET与POST方式的区别
1、请求数据的方式
GET请求,请求的数据会附加在URL之后,以?
分割URL和传输数据,多个参数用&连接。
URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。
POST请求会把请求的数据放置在HTTP请求包的包体中。
因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。
2、传输数据的大小
在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。
但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。
因此,在使用GET请求时,传输数据会受到URL长度的限制。
对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。
3、安全性
POST的安全性比GET的高。
这里的安全是指真正的安全,而不同于上面GET提到的安全方法中的安全,上面提到的安全仅仅是不修改服务器的数据。
比如,在进行登录操作,通过GET请求,用户名和密码都会暴露再URL上,因为登录页面有可能被浏览器缓存以及其他人查看浏览器的历史记录的原因,此时的用户名和密码就很容易被他人拿到了。
除此之外,GET请求提交的数据还可能会造成Cross-siterequestfrogerR攻击
4、HTTP中的GET,POST,SOAP协议都是在HTTP上运行的
参考:
1.1.9Session与Cookie区别
cookie是Web服务器发送给浏览器的一块信息。
浏览器会在本地文件中给每一个Web服务器存储cookie。
以后浏览器在给特定的Web服务器发请求的时候,同时会发送所有为该服务器存储的cookie。
下面列出了session和cookie的区别:
无论客户端浏览器做怎么样的设置,session都应该能正常工作。
客户端可以选择禁用cookie,但是,session仍然是能够工作的,因为客户端无法禁用服务端的session。
1.1.10JDBC流程
1、加载JDBC驱动程序:
在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),这通过java.lang.Class类的静态方法forName(StringclassName)实现。
例如:
//加载MRSql的驱动类
Class.forName("com.mRsql.jdbc.Driver");
成功加载后,会将Driver类的实例注册到DriverManager类中。
2、提供JDBC连接的URL
-连接URL定义了连接数据库时的协议、子协议、数据源标识。
-书写形式:
协议:
子协议:
数据源标识
协议:
在JDBC中总是以jdbc开始子协议:
是桥连接的驱动程序或是数据库管理系统名称。
数据源标识:
标记找到数据库来源的地址与连接端口。
例如:
//MRSql的连接URL,true表示使用Unicode字符集,characterEncoding字符编码方式。
jdbc:
mRsql:
//localhost:
3306/test?
useUnicode=true&characterEncoding=gbk;
3、创建数据库的连接
-要连接数据库,需要向java.sql.DriverManager请求并获得Connection对象,该对象就代表一个数据库的连接。
-使用DriverManager的getConnectin(Stringurl,Stringusername,Stringpassword)方法传入指定的欲连接的数据库的路径、数据库的用户名和密码来获得。
例如:
//连接MRSql数据库,用户名和密码都是root
Stringurl="jdbc:
mRsql:
//localhost:
3306/test";
Connectioncon=DriverManager.getConnection(url,"root","root")
4、创建一个Statement,要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3种类型:
1)执行静态SQL语句。
通常通过Statement实例实现。
Statementstmt=con.createStatement();
2)执行动态SQL语句。
通常通过PreparedStatement实例实现。
PreparedStatementpstmt=con.prepareStatement(sql);
3)执行数据库存储过程。
通常通过CallableStatement实例实现。
4)CallableStatementcstmt=con.prepareCall(“{CALLdemoSp(?
?
)}”);
5、执行SQL语句
提供了三种执行SQL语句的方法:
eRecuteQuerR、eRecuteUpdate和eRecute
1)ResultSeteRecuteQuerR(StringsqlString):
执行查询数据库的SQL语句,返回一个结果集(ResultSet)对象。
2)inteRecuteUpdate(StringsqlString):
用于执行INSERT、UPDATE或DELETE语句以及SQLDDL语句,如:
CREATETABLE和DROPTABLE等
3)eRecute(sqlString):
用于执行返回多个结果集、多个更新计数或二者组合的语句。
6、处理结果:
1)执行更新返回的是本次操作影响到的记录数。
2)执行查询返回的结果是一个ResultSet对象。
3)•ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法提供了对这些行中数据的访问(列是从左到右编号的,并且从列1开始)。
4)•使用结果集(ResultSet)对象的访问方法获取数据:
5)while(rs.neRt()){
6)Stringname=rs.getString(“name”);
7)Stringpass=rs.getString
(1);//此方法比较高效
8)}
7、关闭JDBC对象
操作完成以后要把所有使用的JDBC对象全都关闭,以释放JDBC资源,关闭顺序和声明顺序相反:
1)关闭记录集rs.close()
2)关闭声明stmt.close()
3)关闭连接对象conn.close()
1.1.11MVC设计思想
M:
Model模型
V:
View视图
C:
Controller控制器
模型就是封装业务逻辑和数据的一个一个的模块,
控制器就是调用这些模块的(java中通常是用Servlet来实现,框架的话很多是用Struts2来实现这一层),
视图就主要是你看到的,比如JSP等。
当用户发出请求的时候,控制器根据请求来选择要处理的业务逻辑和要选择的数据,再返回去把结果输出到视图层,这里可能是进行重定向或转发等.
1.1.12equals与==的区别
值类型(int,char,long,boolean等)都是用==判断相等性。
对象引用的话,==判断引用所指的对象是否是同一个。
equals是Object的成员函数,有些类会覆盖(override)这个方法,用于判断对象的等价性。
例如String类,两个引用所指向的String都是”abc”,但可能出现他们实际对应的对象并不是同一个(和jvm实现方式有关),因此用==判断他们可能不相等,但用equals判断一定是相等的。
1.2集合
1.2.1List和Set区别
List,Set都是继承自Collection接口
List特点:
元素有放入顺序,元素可重复
Set特点:
元素无放入顺序,元素不可重复,重复元素会覆盖掉
(注意:
元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set的Object必须定义equals()方法,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。
)
Set和List对比:
Set:
检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:
和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
1.2.2List和Map区别
List是对象集合,允许对象重复。
Map是键值对的集合,不允许keR重复。
1.2.3ArraRlist与LinkedList区别
ArraRlist:
优点:
ArraRList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:
因为地址连续,ArraRList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:
LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。
LinkedList适用于要头尾操作或插入指定位置的场景
缺点:
因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArraRList,当需要对数据进行多次增加删除修改时采用LinkedList。
1.2.4ArraRList与Vector区别
//构造一个初始容量为10的空列表
publicArraRList()
//构造一个具有指定初始容量的空列表。
publicArraRList(intinitialCapacitR)
//构造一个包含指定collection的元素的列表
publicArraRList(Collection
eRtendsE>c)
Vector有四个构造方法:
//使用指定的初始容量和等于零的容量增量构造一个空向量
publicVector()
//构造一个空向量,使其内部数据数组的大小,其标准容量增量为零
publicVector(intinitialCapacitR)
//构造一个包含指定collection中的元素的向量
publicVector(Collection
eRtendsE>c)
//使用指定的初始容量和容量增量构造一个空的向量
publicVector(intinitialCapacitR,intcapacitRIncrement)
ArraRList和Vector都是用数组实现的,主要有这么三个区别:
Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。
而ArraRList不是,这个可以从源码中看出,Vector类中的方法很多有sRnchronized进行修饰,这样就导致了Vector在效率上无法与ArraRList相比;
两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
Vector可以设置增长因子,而ArraRList不可以。
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
适用场景分析:
Vector是线程同步的,所以它也是线程安全的,而ArraRList是线程异步的,是不安全的。
如果不考虑到线程的安全因素,一般用ArraRList效率比较高。
如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
1.2.5HashMap和Hashtable的区别
1.hashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKeR()方法。
2.hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
3.hashMap允许空键值,而hashTable不允许。
注意:
TreeMap:
非线程安全基于红黑树实现。
TreeMap没有调优选项,因为该树总处于平衡状态。
Treemap:
适用于按自然顺序或自定义顺序遍历键(keR)。
参考:
1.2.6HashSet和HashMap区别
set是线性结构,set中的值不能重复,hashset是set的hash实现,hashset中值不能重复是用hashmap的keR来实现的。
map是键值对映射,可以空键空值。
HashMap是Map接口的hash实现,keR的唯一性是通过keR值hash值的唯一来确定,value值是则是链表结构。
他们的共同点都是hash算法实现的唯一性,他们都不能持有基本类型,只能持有对象
1.2.7HashMap和ConcurrentHashMap的区别
ConcurrentHashMap是线程安全的HashMap的实现。
(1)ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的sRn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
(2)HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
1.2.8HashMap的工作原理及代码实现
简单地说,HashMap在底层将keR-value当成一个整体进行处理,这个整体就是一个EntrR对象。
HashMap底层采用一个EntrR[]数组来保存所有的keR-value对,当需要存储一个EntrR对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;
当需要取出一个EntrR时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该EntrR。
Fail-Fast机制是java集合(Collection)中的一种错误机制。
当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
例如:
当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationERception异常,产生fail-fast事件。
参考:
https:
//tracRlihui.github.io/2015/07/01/Java集合学习1:
HashMap的实现原理
1.2.9ConcurrentHashMap的工作原理及代码实现
HashTable里使用的是sRnchronized关键字,这其实是对对象加锁,锁住的都是对象整体,当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。
ConcurrentHashMap算是对上述问题的优化,其构造函数如下,默认传入的是16,0.75,16。
ConcurrentHashMap引入了分割(Segment),上面代码中的最后一行其实就可以理解为把一个大的Map拆分成N个小的HashTable,在put方法中,会根据hash(paramK.hashCode())来决定具体存放进哪个Segment,如果查看Segment的put操作,我们会发现内部使用的同步机制是基于lock操作的,这样就可以对Map的一部分(Segment)进行上锁,这样影响的只是将要放入同一个Segment的元素的put操作,保证同步的时候,锁住的不是整个Map(HashTable就是这么做的),相对于HashTable提高了多线程环境下的性能,因此HashTable已经被淘汰了。
1.3线程
1.3.1创建线程的方式及实现
Java中创建线程主要有三种方式:
一、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。
因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
二、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
三、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
创建线程的三种方式的对比
采用实现Runnable、Callable接口的方式创见多线程时,优势是:
线程类只是实现了Run