1.JAVA中的几种基本数据类型是什么,各自占用多少字节。
答:
int 32bit
short 16bit
long 64bit
byte 8bit
char 16bit
float 32bit
double 64bit
boolean 1bit
2.String类能被继承吗,为什么。
答:String 是final类,String也是java中真正的Immutable。
3.String,Stringbuffer,StringBuilder的区别。
答:1.String类是不可变的,所谓不可变意思就是创建一个类后任何对String的改变都 会引发新的String对象的生成,应为String是不可变的因此不存在线程安全的问题;2.Stringbuffer,StringBuilder是可变的,不会每次都生成新对象,但是有容量大小,默认容量为16,当超过最大容量时会扩容,为了减少扩容的开销,新建时尽量设置容量大小;3.StringBuffer和StringBuilder类的区别是StringBuffer是线程安全,打开StringBuffer会发现各种修改数据的方法都加上 synchronized 关键字实现的,简单暴力,StringBuilder 是 Java 1.5 中新增的,在能力上和 StringBufer 没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。4、我们实际使用中也不用过于纠结是使用String还是StringBuffer与StringBufer,现代JDK中编译时就会进行优化会将字符串拼接(+号拼接)优化为StringBuffer,甚至如"a"+"b"这样的凭借会直接编译优化为"ab",因此还是优化考虑代码的可读性和美观,但是通过for循环凭借字符串传时请使用StringBuffer或StringBufer。
4.ArrayList和LinkedList有什么区别。
答:1、数据结构不同,ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构,ArrayList数据满的时候需要扩容,但LinkedList不需要,但是linkedList需要维护前后节点信息,因此会有额外的内存开销;2、当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高;如果能预估ArrayList大概长度,设置默认容量,防止频繁扩容,优先是用ArrayList,此时查询、新增性能都比较好,但如果存在频繁删除操作时优先使用linkedList;
5.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。
答:顺序为:父类静态变量 ->父类静态代码块->子类静态变量->子类静态代码块、
父类非静态变量(父类实例成员变量)->父类构造函数->子类非静态变量(子类实例成员变量)->子类构造函数。
6.用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
答:1、HashTable、HashMap、TreeMap都是常见的Map实现都是以键值对方式存储,HashTable本身是同步的不支持null的键和值,由于同步导致性能开销,一班不推荐使用,默认初始容量为11,扩容2*原数组长度+1;HashMap不是同步的,支持null键和值,在数量比较小的时候HashMap get、put都能达到常数时间的性能,所以是绝大多数利用键值存取场景的首选,因为HashMap是采用数组建单向链表的是数据结构,因此在hash冲突较多的情况小查询性能骤降,因此JDK8开始加入了红黑树,默认链表长度超过8时变为红黑树这样查询、新增都能达到O(log n)的性能,HashMap有两个关键的属性即负载因子(默认0.75默认)和容量(默认16)容量只能是2的幂数,因此扩容都是翻倍扩;LInkedHashMap和HashMap类似,加入了双向链表,遍历顺序符合插入顺序;TreeMap 则是基于红黑树的一种提供顺序访问的 Map ,和 HashMap 不同,它的 get 、 put 、 remove 之类操作都是 O(log n)的时间复杂度,具体顺序可以由指定,默认是按键值的升序排序;2、并发下推荐使用ConcurrentHashMap, Collections.synchronizedMap也可以使用来获取一个同步包装容器,但是性能较差不推荐使用,ConcurrentHashMap 在JDK1.7是基于分段锁实现并发控制,也就是将内部进行分段(Segment),里面则是HashEntry的数组,和HashMap类似,哈希相同的条目也是以链表形式存放;JDK1.8 请看第7题;
7.JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
答:分段式锁的粒度任然很大,默认16的分段并不能带来很好的并发性能,JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,在没有hash冲突的情况下直接使用cas来操作,没有锁的性能开销,当hash冲突时,则直接用链表第一个object加锁,这里加的锁是synchronized,虽然效率不如 ReentrantLock, 但节约了空间,整个看起来就像是优化过且线程安全的HashMap。
8.有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。
答:常见linkedHashMap、TreeMap;LinkedHashMap是基于链表来实现数据插入有序的;TreeMap是基于比较器Comparator来实现有序,TreeMap以红黑树的形式存储,本身就是一个二叉查找树。
9.抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
答:1、接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到 API 定义和实现分离的目的。接口,不能实例化;不能包含任何非常量成员,任何 feld 都是隐含着 public static final 的意义;同时,没有非静态方法实现,也就是说要么是抽象方法,要么是静态方法。 抽象类是不能实例化的类,用 abstract 关键字修饰 class ,其目的主要是代码重用。除了不能实例化,形式上和一般的 Java 类并没有太大区别,可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关 Java 类的共用方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。 Java 8以后,接口也是可以有方法实现的,个人认为以后抽象类和接口应该却别越来越少甚至合并。2、类只能继承一个类,但可以实现多个接口,接口可以继承多个接口,不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢?但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。
10.继承和聚合的区别在哪。
答:继承他是is-a的关系,指一个类继承另外一个类的功能;聚合他是has-a。
11.IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。
答:
BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程并处理,如果这个连接不做任何事情会造成不必要的开销,当然可以通过线程池机制改善;
**NIO **:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理
NIO2(AIO):异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理;
reactor模型:NIO基于Reactor,当socket有流可读或可写入socket,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。也就是,不是一个链接就要对应一个处理线程,而是一个有效请求对应一个线程,当连接没有数据时,是没有工作线程来处理的
12.反射的原理,反射创建类实例的三种方式是什么。
答:原理:类的字节码加载到JVM后,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射创建类实例的三种方式:
- Class.forName(“com.A”)
- new A().getClass()
- A.class
13.反射中,Class.forName和ClassLoader区别 。
答:Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的,但是Class.forName()会对加载的类进行初始化,会执行类中的静态代码块,以及对静态变量的赋值等操作。classLoader仅仅就是将.class文件加载到jvm。
14.描述动态代理的几种实现方式,分别说出相应的优缺点。
答:主要的有jdk动态代理和cglib动态代理两种方式:
jdk动态代理
优点:1、最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比cglib更加可靠;2、平滑进行JDK版本升级,而字节码类库通常需要进行更新以保证在新版Java上能够使用。3、代码实现简单。
缺点:目标类必须实现一个接口,使用范围受限;
cglib:
优点:1、用更高性能的字节码操作机制,性能稍高,类似还有ASM、Javassist等;
2、有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似cglib动态代理就没有这种限制;
3、只操作我们关心的类,而不必为其他相关类增加工作量;
缺点:1、由于cglib是基于继承的方式实现类的动态代理,因此无法实现对final方法的代理,也不能完全算缺点的,只是个坑,大家注意即可;
15.动态代理与cglib实现的区别。
答:参考考第14题目,这里补充一点:jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的,对指定的目标生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
16.为什么CGlib方式可以对接口实现代理。
答:cglib动态代理是继承并重写目标类,所以目标类和方法不能被声明成final,而接口是可以被继承的。
17.final的用途。
答:1、使用fnal修饰参数或者变量,也可以清楚地避免意外赋值导致的编程错误,同时提升代码可读性;2、final变量产生了某种程度的不可变(immutable)的效果,所以,可以用于保护只读数据,尤其是在并发编程中,因为明确地不能再赋值fnal变量,有利于减少额外的同步开销,也可以省去一些防御性拷贝的必要;3、被final修饰的方法,JVM会尝试为之寻求内联,这对于提升Java的效率是非常重要的。因此,假如能确定方法不会被继承,那么尽量将方法定义为final的。
18.写出三种单例模式实现 。
答:
懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
双重锁模式
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类单例模式
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return Inner.instance;
}
private static class Inner {
private static final Singleton instance = new Singleton();
}
}
枚举单例模式(effective java中推荐)
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
//使用方式
Singleton.INSTANCE.doSomething();
19.如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣。
答:其实前半句的问题有点懵,直接父类中重写hashcode和equals实现即可,但是父类的hashcode和equals并不一定能满足子类的要求,比如所有的对象都继承Object,默认使用的是Object的equals方法,在比较两个对象的时候,是看他们是否指向同一个地址,但是我们的需求是对象的某个属性相同,就相等了,而默认的equals方法满足不了当前的需求,所以我们要重写equals方法。
如果重写了equals 方法就必须重写hashcode方法,否则就会降低map等集合的索引。
20.请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设计中的作用。
答:OO设计理念:抽象、封装、继承、多态;封装也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏,所以我们可以通过public、private、protected、default 来进行访问控制;
21.深拷贝和浅拷贝区别。
答:浅拷贝基本类型拷贝值,引用类型只拷贝引用地址;深拷贝引用类型也是拷贝他的值,重新生成的对像,clone出来对象与原对象互不影响。
22.数组和链表数据结构描述,各自的时间复杂度。
答:数组是将元素在内存中连续存放,由于每个元素占用内存相同,可以通过下标迅速访问数组中任何元素。插入、删除都可能需要移动的元素,最差的时间复杂度是O(N),根据下标获取元素的复杂度是O(1),且数组的大小是固定不可变的。链表恰好相反,链表中的元素在内存中不是顺序存储的,而是通过存在元素中的指针联系到一起,需要额外的空间来存储相连节点的指针,链表增加和删除时只要修改元素中的指针即可,时间复杂度为O(1),如果应用需要经常插入和删除元素你就需要用链表。
23.error和exception的区别,CheckedException,RuntimeException的区别。
答: Exception 是程序正常运行中可以预料的异常,可能并且应该被捕获,并进行相应处理。Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
CheckedException 是可检查异常,需要程序显式捕获处理;
RuntimeException 是不检查异常,不检查异常就是所谓的运行时异常,类似NullPointerException 、 ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。
异常处理的基本原则:1、 尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常;2、 不要生吞(swallow)异常;
24.请列出5个运行时异常。
答:ClassCastException(类转换异常)、IndexOutOfBoundsException(数组越界)、NullPointerException(空指针)、ArithmeticException(算术运算异常)BufferOverflowException(内存溢出)、ArrayStoreException (向数组中存放与声明类型不兼容对象异常)
25.在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么。
答:不可以;类加载器层次:自定义类加载器 >> 应用程序类加载器 >> 扩展类加载器 >> 启动类加载器;双亲委派机制:如果一个类加载器收到了类加载的请求,它首先不会自己尝试去加载这个类,而是把这个请求委派给父类加载器,每一个层次的类加载器都是如此,因此所有的加载请求最终到达顶层的启动类加载器(BootStrap),只有当父类加载器反馈自己无法完成加载请求时,子类加载器才会尝试自己去加载,所以BootStrap(启动类)加载器加载jdk里面的java.lang.String类,而自定义的java.lang.String类永远不会被加载到。
26.说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重新实现这两个方法。
答:Object的对象equals方法是判断两个对象是否指向同一个地址,并不一定能满足我们的需求,比如String就重写了Object的equals方法,如果重写了equals 方法就必须重写hashcode方法,否则就会降低map等集合的索引速度。
27.在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。
答:我们在编写程序时,经常遇到两个模块的功能非常相似,只是一个是处理int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同,当然我们也可以用一个通用类型处理,但这样都把问题留个运行时了,这样并不安全可读性也差,因此这个时候泛型就出现了,类在进行定义的时候可以使用一个标记,此标记就表示类中属性或者方法以及参数的类型,标记在使用的时候才会确定下来,而且泛型的好处是在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率,避免在运行时出现 ClassCastException。
28.这样的a.hashcode() 有什么用,与a.equals(b)有什么关系。
答:hhashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数,这个哈希码的作用是确定该对象在哈希表中的索引位置,因为hashCode() 在Object类中,所以任何类都可以重写hashCode() ,但是如果哪个类没有重写hashCode() ,当调用hashCode() 默认行为是对堆上的对象产生独特值。
hashCode()与equals()的相关规定:
- 如果两个对象相等,则hashcode一定也是相同的;
- 两个对象相等,对两个对象分别调用equals方法都返回true;
- 两个对象有相同的hashcode值,它们也不一定是相等的;
- equals方法被覆盖过,则hashCode方法也必须被覆盖;
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该类的两个对象无论如何都不会相等;
29.有没有可能2个不相等的对象有相同的hashcode。
答:有可能,最粗暴的办法就是重新类的hashcode方法equals,让hashcode相等,equals不相等。
30.Java中的HashSet内部是如何工作的。
答:HashSet 的内部采用 HashMap来实现,由于 Map 需要 key 和 value,所以HashSet中所有 key 的都有一个默认value,类似于 HashMap,HashSet 不允许重复的 key,只允许有一个null key,意思就是 HashSet 中只允许存储一个 null 对象;HashSet的部分源代码:
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
31.什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
答: 1、Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程;2、java 通过 java.io.ObjectOutputStream、java.io.ObjectInputStream API来完成序列化和反序列化;3、当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的。如何做到呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象;也可以实现数据的持久化,通过序列化可以把数据永久地保存到硬盘上;4、序列化时是通过判断类的serialVersionUID来验证版本一致性的,如果修改了serialVersionUID的值就会导致反序列化失败。
32.java8的新特性。
答:主要新特性有:
- 接口的默认方法
- Lambda 表达式
- 函数式接口
- 方法与构造函数引用
- 更好的类型推断
- Streams
- Date API
- Optional
- Metaspace
感谢阅读,都是根据自己理解写的,有不对的地方欢迎各位大佬指正,也希望能帮助到更多的人。
注:(JVM篇)持续更新中
题目来自:https://blog.csdn.net/weixin_44946117/article/details/90607041
部分内容来自网络整理,如果侵权请联系删除。