Java后台开发常见面试题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java后台开发常见面试题相关的知识,希望对你有一定的参考价值。
八种基本数据类型的大小,以及他们的封装类
整数型: byte 1个字节 -128~127 封装类:Byte
short 2个字节 -32768~32767 Short
Int 4个字节 -2147483648~2147483647 Integer
long 8个字节 -2的63方~2的63次方-1 Long
浮点型: float 4个字节 单精度 Float
Double 8个字节 双精度 Double
布尔类型: boolean 4个字节 true或false Boolean
字符类型: char 2个字节 Character
引用数据类型:
分为3种,类、接口和数组
Switch能否用String做参数
在jdk7以前,switch只支持byte、int、short、char或者对应的包装类和枚举。
equals和==的区别
a) 对象类型不同 equals是超类Object的方法,而==是操作符
b) 比较类型不同 equals用来检测两个对象是否相同,即两个对象的内容是否相等,==比较引用数据类型和基本数据类型时具有不同的功能。==比较的是变量(栈)内存中存放的对象的内存地址,用来判断两个对象的地址是否相同,即是否指向同一对象,比较的是真正意义上的指针操作,而equals用来比较的是两个对象的内容是否相同,适用于所有对象。
c) 注意:如果没有对该对象进行覆盖的话,调用的仍然是超类Object的方法,而Object中的equals方法返回的是==的判断。
自动拆装箱和常量池
a) Java自动将原始数据类型值转换成对应的对象,比如将int类型的变量转换成Integer类型,自动装箱时调用valueOf将原始数据类型转换成对应的对象。
b) 反之,将Integer对象装换成int类型值,这个过程叫拆箱,自动拆箱通过调用类似intValue、doubleValue这类的方法将对象转换成对应的原始数据类型。
c) Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
i. 静态常量池:即.*class文件中的常量池,class文件中的常量池不仅仅包含字符串,还包含类、方法的信息,占用class文件绝大部分空间。
ii. 而运行时常量池:则是jvm虚拟机在完成类装载过程后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池就是指方法区中的运行时常量池。
Object中有哪些公用方法?
a) Clone:实现对象的浅复制,只有实现了cloneable接口才可以调用此方法,否则会抛出CloneNotSupportedException异常。
b) Equals:在Object中和==是一样的,子类一般需要重写此方法。
c) Hashcode:该方法用于哈希查找,重写了equals方法一般都要重写hashcode方法,这个方法在一些具有哈希功能的collection中用到。
d) getClass:获得运行时的类型。
e) wait:是当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是说具有该对象的锁!
f) Notify:唤醒在对象上等待的某个线程。
g) notifyAll:唤醒该对象上等待的所有线程。
h) toString:转换成字符串,一般子类都要重写此方法,否则打印句柄。
Java的四种引用,强弱软虚。
a) 强引用:最常见,把一个对象赋给引用数据类型则为强引用。如果内存中不足,java虚拟机将会跑出OutOfMemoryError错误,从而将程序异常停止强引用的对象是不可以gc回收的,不可以随意回收具有强引用的对象来解决内存不足的问题。在java中,强引用是一种默认的状态,除非Java虚拟机停止工作。
b) 软引用:如果一个对象具有软引用,内存空间足够,垃圾回收就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序继续使用。软引用用来实现内存敏感的高速缓存,比如网页缓存或图片缓存,使用软引用能防止内存泄露,增强程序的健壮性。
c) 弱引用:用来描述非必需对象的,当jvm进行垃圾回收的时候,无论内存是否充足,都会回收被弱引用关联的对象。
d) 虚引用:他不影响对象的生命周期,如果一个对象与虚引用关联,则跟没有与之关联一样,在任何时候都可能被垃圾回收器回收。
hashcode的作用
比较两个对象是否相等,重写的equals方法一般比较全面,比较复杂,效率比较低,而采用hashcode方法进行对比,只需要生成一个hashcode进行比较就可以了!
Hashmap中的hashcode的作用
a) Hashcode的存’用于查找的快捷性,如hashtable、hashmap等,hashcode是在散列结构中确定对象的地址。
b) 如果两个对象相同,就是适用equals方法,那么这两个对象的hashcode一定要相同。
c) 如果对象的equals方法被重写,那么对象的hashcode也尽量重写,并且产生hashcode使用的对象,一定要和equals放法使用的一致,否则会违反上面的第二点。
d) 两个对象的hashcode相同,并不代表这两个对象就相同,也就是不一定适用equals方法,只能说明这两个对象在散列存储结构中,如hashtable,他们“存放在同一个篮子里”。
为什么重载hashcode方法?
一般不需要重载hashcode方法,只有当类放在hshtable、hashmap、hashset等hash结构的集合时,才会重载hashcode。就hashmap来说,好比hashmap就是一个大内存块,里面有许多小内存块,小内存里面就是一系列的对象,可以利用hashcode查找小内存块,,所以当equals相等时,hashcode必需相等,而且如果是Object对象,必须重载hashcode和equals方法。
Arraylist、linkedlist和vector的区别?
a) Arraylist:底层数据结构是数组,查询快、增删慢,线程不安全。
b) Liskedlist;底层数据结构是链表,查询慢、增删块,线程不安全。
c) Vector:底层是数组,查询慢、增删慢,线程安全。
String、stringbuffer与stringbulder的区别?
a) String是不可变的对象,因此每次对string类型进行改变的时候,就等于生成了一个新的对象,然后蒋指针指向一个新的string的对象,因此经常改变内容的字符串最好不用string,因为每次生成对象都会对系统性能产生影响,特别是当内存中无引用对象过多时,jvm的gc就会开始工作,那么速度一定会非常慢。
b) 而stringbuffer结果就完成不一样了,每次结果都会对stringbuffer本省进行操作,而不是生成新的对象,再改变对象的引用。是线程安全的。
c) Stringbuildere是jdk5以后新增的,其用法和stringbuffer完全一致,但它是线程不安全的,在单线程中是最佳的,因为不需要维护线程的安全。
Map、set、list、queue、stack的特点与用法。
a) Map是键值对,键key是唯一不能重复的,一个键对应一个值,值可以重复,treemap可以保证顺序,hashmap不保证顺序,即是无序的,map可以把key和value单独抽取出来,其中keyset()方法可以将所存的key抽取成一个set。而valuses方法可以将map中所有的value抽取成一个集合。
b) Set:不包含重复元素,且最多包含一个null元素,只能用iterater实现单向遍历, set没有同步方法。
c) List:有序的可重复集合,可以在任何位置增加和删除元素。用Iterator实现单向遍历,也可以用listIterator实现是双向遍历。
d) Queue:遵从先进先出原则,使用时尽量避免add()和remove()方法,而只是使用offer()来添加元素,使用poll来移除元素,它的优点是可以通过返回值来判断是否成功,LinkedList实现Queue接口,Queue通常不允许插入null元素。
e) Stack:遵从先进后出原则,stack继承自vector,它通过五个操作对类vector进行扩展,允许将向量视为堆栈,它提供push和pop操作,以及取堆栈顶点的peek()方法,测试堆栈是否为空的empty()方法等。
f) 用法:如果涉及堆栈、队列等操作,建议使用list,对于快速插入和删除元素,建议使用linkedlist,如果,快速随机访问元素,建议使用ArrayList。
Hashmap和hashtable的区别?
a) 都是map接口的实现类,都是基于hash表的实现类。
b) Hashmap集合线程不安全的类,不同步,执行效率高,允许键和值是null。
c) Hashtable集合是线程安全的类,同步,执行效率低,不允许键和值为null。
Jdk7和jdk8中的hashmap的实现。
a) Jdk7:hashmap底层维护一个数组,数组中的每一项都是一个entry,我们向hashmap中放置的对象实际上存储在该数组中,map中的key和value则以entry的形式存放在数组中,而这个key放在数组的哪一个位置上,是通过key的hashcode来计算的。
b) Jdk8:采用(位桶+链表)红黑树的方式,也是非线程安全的。
Hashmap和hashConcurrentHashmap的区别,hashmap的底层源码。
Hashmap是线程非安全的,效率比较高,hashtable和ConcurrentHashmap是线程安全的,效率比hashmap差一点,但ConcurrentHashmap采用了更高级的分段锁机制,具体可以理解为把hashmap拆分为n个小的hashtable,根据key。Hashcode()来决定把key放到哪个hashtable中,在concurrenthashmap中,就是把map分为n个segment,put和get时,都是key.hashcode()算出放到哪个segment中。
接口和继承的区别?接口内可以定义常量吗?继承可以继承父类的那些东西?
a) 区别:
i. 标识符不同,interface和extends
ii. 接口支持多继承,而不支持继承的继承
iii. 接口中只能定义全局变量和抽象变量,而继承中可以定义属性、方法、常量、变量等。
iv. 某接口被实现,在类中一定要实现接口所有的方法,而继承只需要用哪一个就调用哪一个。
b) 接口不可以定义常量。
c) 继承可以继承父类所有的东西(成员方法、成员变量、包括私有)。
如何修改父类中以private关键字修饰的变量?
如果父类中有public的set方法,就可以通过set方法修改父类中以private修改的变量。
Java中public、private 、protect、default的区别?
a) Public:java中访问限制最宽的修饰符,即公共的,被修饰的类、属性、方法允许跨类访问,甚至可以跨包访问。
b) Private:是java中访问限制最窄的修饰符,被private修饰的类、属性、方法只能被本类的对象进行访问,其子类不能访问,更不允许跨包访问。
c) Protec:介于public和private之间的一种访问修饰符,一般称受保护的,被其修饰的类、方法、属性只能被本类和子类进行访问,即使子类不在同一个包下。
d) Default:意为默认的,即不加修饰符,该模式下只能同一个包下进行访问。
数据库锁的问题?如何上锁?是不是行锁?
a) 当多个用户同时对数据库进行操作时,会带来数据不一致的问题,所以锁主要是用于多用户环境下保证数据库完整性和一致性.
b) 锁分类:从数据库系统来看:排它锁、悲观锁、更新锁
从程序员角度来看:一种是乐观锁,一种是悲观锁。
i. 悲观锁:很悲观,每次去拿数据的时候,都以为别人会修改,所以在每次拿数据的时候,都会上锁,这样别人拿这个数据就会block,直到它合锁。具有强烈的排他性和独占性,悲观锁的实现依靠数据库提供的机制,也只有数据库底层提供的机制才能保证数据访问的排他性。传统的关系型数据库用到了很多这同样的机制,比如行锁、表锁、读锁、写锁等,都是在操作前加上锁。
ii. 乐观锁:很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。悲观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write—condition机制的其实都是提供的乐观锁。
c) 按作用性质分:
i. 共享锁:也叫读锁,用于所有只读操作,不能同时增删改,可以查。
例如:select * from 表 lock in share mode;
ii. 排它锁:也叫读锁,表示对数据进行写操作,一个事务对象施加了排它锁,其他事务就不能加锁了。
例如:查询时加排它锁,别人不能加共享锁和排它锁。
Select * from 表 for update;
仅允许一个事务封锁此表,其他事务必须等到此锁被释放才能访问。
排它锁(x锁)一直到事务结束才能释放。
iii. 更新锁:允许其他事务读,但不允许在施加x锁和u锁,当要被读取的对象要被更新的时候,则升级为x锁,防止死锁,如果一个数据在修改前直接申请更新锁,在修改时再升级为排它锁,就可以避免死锁。
d) 按范围分:
i. 行锁:锁的范围是行级别,数据能够确定哪些行需要锁的情况下使用行锁(inodb),如果在不知道会影响到哪些数据的时候就会使用表锁
例如:一个用户表有主键id和birthday。
Update …where id=? 行锁
Update … where birthday=? 表锁
ii. 表锁:锁的范围是整张表。 MyISAM
22.事务的隔离性
a) 隔离性级别越低,并发性越好,但数据库的一致性就越差。
隔离级别越高,并发性越差,但数据库的一致性高。
b) 由低到高
读未提交<读提交<可重复读(默认)<序列化读
c) 错误的级别由低到高:
脏读、不可重复读、幻读
脏读:两个事物,一个事务先修改,另一个事务读,结果是修改前的结果。
不可重复读:两个事物,一个先读是一个结果,一个后修改,再读,又是一个结果。
幻读:第一个事务表中有10、20、30、40几个部门,第二个事务插入表中50的部门,然后提交,第一个事务插入50部门,主键冲突。
23.能否用一个sql语句解决库存判断问题(非查询语句)。
用if语句:if(exper1,exper2,exper3)
如果exper1的值是true,则返回是exper2的值,如果exper1的值是false,则返回exper3的值。
例如:id name number
1 苹果 300
2 梨 100
Update fruit set number=if(number>200,number,0)where if=’1’;
即如果苹果的数量大于200,就不变,否则,number改为0,即就可以判断内存是否充足。
24.红黑树
a) 特性:
i. 每个节点都是黑色或者红色。
ii. 根节点是黑色。
iii. 定义null为黑色。
iv. 如果某个子节点是红色,那么它的两个儿子都是黑色,且父节点一定是黑色。
v. 对于任意节点,它到叶子节点的每一条路径都包含相同数目的黑色节点
性质5称之为黑高。
b) 相对平衡:
i. 若H(left)>=H(right),则H(left)=2*H(right)+1
ii. 但BH(left)=BH(right),H(left)<H(right)同理。
c) 在调整节点p之前,必需保证p的左子树left、右子树right都已经是rbt。
i. 多个子问题成立->某个总问题成立
ii. 插入调整:删除调整均是由底向上
d) Rbt的插入调整(默认是红色)
i. 无需调整
- X为根节点,将x由红染黑
- 父亲节点是黑色
ii. 仅仅考虑父亲节点p是红色,由于性质4,爷爷节点必定是黑色,分为3中:
1. Case1:y为黑色,x可左可右;p、y染黑,g染红;x回溯至g
2. Case2:y为黑色,x为右孩子,左旋p,x指向p,转为case3
3. Case3:y为黑色,x为左孩子,p染黑,g染红,右旋g结束。
Rbt的插入调整最多2次
右旋转:逆时针旋转两个节点,使父亲节点被右孩子取代,而自己变成左孩子。
左孩子:顺时针旋转两个子节点,使父亲节点被左孩子取代,自己变成右孩子。
25.Session是什么?和cookie的区别?
a) Cookie通过客户端记录信息确认身份,session是通过服务端的信息确认身份。
b) 客户端浏览器访问服务器的时候,服务端把客户端的信息以某种形式记录在服务器上,这就是session,用户与服务器建立连接的同时,服务器自动分配一个sessionid。
c) 区别:cookie不×××全,别人可以分析本地存在的cookie并进行cookie欺骗,考虑到安全应当使用session。
-
有哪些排序算法?说一下堆排序?时间复杂度是多少?
a) 直接插入排序:第一个元素已经排序,第二个与第一个元素进行比较,第一个大,第一个则与第二个交换位置,第三个分别于第一个和第二个比较,以此类推。 时间复杂度:O(n*n) b) 希尔排序:按步长grep进行分组,然后将每组元素利用直接插入排序的方法进行排序;每一次结束后再将步长grep减半,循环上述操作,直到步长grep=1,利用直接插入排序,完成排序。 时间复杂度:O(log n) c) 简单选择排序:首先在未排序序列中找到最小(或最大)的元素,放在排序序列的起始位置,然后再从未排序序列中找到最小(最大)的元素,放在已排序序列的后面。 时间复杂度:O(n*n) d) 堆排序:堆排序过程就是将待排序序列构造成一个堆,选出堆中最大的移走,再把剩下的整成堆,找出最大的在移走,重复直至有序。 i. 每次将根节点与左右节点最大的进行交换,直到根节点最大,然后把根节点放在最后一个位置,然后再次执行循环,找到最大值放在长度-1的位置。 ii. 时间复杂度:O(n log n) e) 冒泡排序:每次比较两个元素,把小的放在前面。 时间复杂度:O(n*n) f) 快速排序:把一个序列分为两个子序列,步骤可分为: i. 从数列中选出一个元素,作为基准。 ii. 重新排序数列,所有比基准序列小的元素放在基准前面,所有比基准大的元素放在基准后面,相同的放在任意一边,在这个分区结束后,该基准处于中间位置。 iii. 递归将小于基准的子序列和大于基准的子序列排序。 iv. 即:先对大序列进行排序,大序列分为两个子序列,然后递归调用方法对这两个序列进行排序。 g) 归并排序:将两个或两个以上的有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列都是有序的,然后再把整个序列合并成整体有序序列。 时间复杂度:O(nlogn) h) 基数排序:将所有待比较的数(正整数统一为同样的数位长度),数位数不足的前面补0,然后从低位开始,依次进行一次排序,这个序列从最低位排序一直到最高位排序完成以后,数列将变成一个有序序列。 时间复杂度:O(d*(n+r)) D为位数,r为基数,n为数据个数
27.Statement和preparestatement的区别?
a) 代码的可读性和可维护性preparestatement更好。
b) Preparestatement尽最大可能提高性能。
c) 提高安全性,preparestatement防止sql注入。
28.NIO和IO到底有什么区别?有什么关系?
a) NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的,肯定NIO的效率比IO的效率高出好多。
b) NIO不是和IO一样用Ouputstream和Inputstream输入流形式来处理数据,但是基于这种流的形式,而是采用通道和缓冲流的形式来处理数据的。
c) NIO通道可以是双向的,IO中的流只能是单向的。
d) 还有就是NIO的缓冲区(其实也是一个字符数组)还可以进行分片,可以建立只读缓冲区、直接缓冲区和间接缓冲区,只读缓冲区很明显就字面意思,直接缓冲区是为了加快I/O速度,而以一种特殊的方式分配其内存的缓冲区。
29.数据库索引的分类,索引底层的实现,说一下b+树,b-树?
a) 索引的作用:
i. 提高查询速度
ii. 确保数据的唯一性
iii. 可以加快表与表之间的连接,实现表和表之间的完整性。
iv. 使用分组和排序字句进行数据检查时,可以减少分组和排序的时间。
v. 全文检索字段进行搜索优化。
b) 分类;
i. 主键索引(PRIMAY_KEY):确保数据记录的唯一性,一个表只有一个。
ii. 唯一索引(UNIQUE):避免一个表中某些数据列中的值重复。可以有多个。
iii. 常规索引(INDEX):快速定位特定数据。
iv. 全文索引(FULLTEXT):快速定位特定数据,只能用于MyISAM类型的数据表。
c) 索引底层实现:把平衡树当做数据表默认的索引数据结构,平衡树也就你、b+tree或者b-tree。
d) B-tree:适用于外查找的树,是平衡树的多叉树。
i. 一个m阶b树是一颗平衡的m路搜索树,它或者是空树,或者满足下列性质:
1. 定义任意非叶子节点最多有m个儿子,且m>=2;
2. 根节点的儿子数[2,m];
3. 除根节点以外的叶子节点的儿子数{m/2,m};
4. 每个节点存放m/2-1(向上取整)和至多m-1个关键字(至少2个)。
5. 非叶子节点的关键字个数=指向儿子的指针个数-1;
6. 非叶子节点的关键字:k[1]、k{2}…k[m-1],且k{i}<k{i+1}。
7. 非叶子节点的指针:p{1} 、p{2} 、p{3}、 p{4} …p{m},其中p[1】指向小于k[1]的子树,p[m]指向关键字属于k{i}和k{i+1}之间的子树。
8. 所有子树节点位于同一层。
ii. B-树的特性:
1. 关键字集合分布在整棵树中。
2. 任何一个关键字出现且只出现一个节点中。
3. 搜索可能在非叶子节点结束。
4. 其搜索性能等价于在在关键字全集中做一次二分查找。
5. 自动层次控制。
e) B+树:
i. B+树的定义:
1. 其定义与b-树基本相同,除了:
2. 非叶子节点的子树指针与关键字个数相同。
3. 非叶子节点的子树指针p[i],指向关键字属于k[i],k[i+1]的子树。
4. 为所有关键字增加一个键指针。
5. 所有关键字在叶子节点出现。
ii. B+树的特性:
1. 所有关键字都出现叶子节点的链表中(稠密索引)且链表中的关键字恰好是有序的。
2. 不可能在非叶子节点命中。
3. 非叶子节点相当于叶子节点的索引(稀疏索引),叶子节点相当于存储数据(关键字)的数据层。
4. 更适合文件索引系统。
以上是关于Java后台开发常见面试题的主要内容,如果未能解决你的问题,请参考以下文章