Java基础
-
JAVA中的几种基本数据类型是什么,各自占用多少字节。
- byte 1字节、short 2字节、int 4字节、long 8字节、float 4字节 、double 8字节、char 2字节、boolean 1字节
-
String类能被继承吗,为什么。
- String 不能被继承,因为其实被final修饰的。其内部维护了一个char[]数组,该数组也是被final修饰的。
-
String,Stringbuffer,StringBuilder的区别。
- String 是不可变的、StringBuffer和StringBuilder继承自AbStractStringBuilder,其内部char[] 不是final的,即他们俩是可变的
- String 是线程安全的、StringBuilder 不是线程安全的、StringBuffer 对其方法加同步锁 是线程安全的。
-
ArrayList和LinkedList有什么区别。
- ArrayList 基于数组实现,LinkedList 基于双向链表,其在不同操作下有性能区别
- 添加元素时 LinkedList 只需操作相关元素即可,速度占优。ArrayList 默认添加元素时,如果默认空间不够需要扩容,其出发System.arraycopy 复制元素,扩展空间,性能会有影响。如果在指定位置下插入元素ArrayList 需要复制后续元素,性能也会有影响
- 删除元素时 LinkedList 表现与添加元素一致,而ArrayList需要进行数据重组,开销较大。
- 查询时,因数组是连续内存空间,通过首地址+偏移量可直接计算到元素位置,链表为分散内存通过只想链接,查找时时通过遍历查找,速度要比Arraylist慢
- ArrayList 基于数组实现,LinkedList 基于双向链表,其在不同操作下有性能区别
-
讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。
- 父类静态变量->父类静态代码块->子类静态变量->子类静态代码块->父类非静态变量->父类构造函数->子类非静态变量->子类构造函数
-
用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
- hashmap: 存储方式:数组+链表+红黑树,默认容量16,负载因子0.75,扩容*2,其内部实现非线程安全,计算index为 hash & (length -1),允许null key 和 value
- hashtable: 数组+链表,默认容量11,扩容*2+1,计算index为取模,修改值时锁住整个map,其为线程安全,不允许null值
- ConcurrentHashMap:其为hashmap的安全实现,再修改其值时采用CAS+Synchronized锁住部分区域保证线程安全,读取数据时在不加锁前提下获取最新数据。
-
JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。
- 放弃分段锁引入cas 是为了降低锁的力度,放弃ReentrantLock 是因为java8后 synchronize进行了优化,其占用的内存更小,节省空间
-
有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。
- TreeMap 基于key 升序,通过比较器Comparator 实现的
- LinkedHashmap 数据插入顺序,其基于链表保证数据顺序。
-
抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。
- 接口抽象类区别
- 抽象类:1、抽象类使用abstract修饰 2、抽象类不能实例化 3、含有抽象方法的类是抽象类,必须使用abstract关键字修饰 4、抽象类中可以有 抽象方法 和具体方法 5 抽象方法只有方法体没有具体实现
- 接口:1、接口使用interface修饰 2、接口不能别实例化 3、一个类只能继承一个类,但是可以实现多个接口 4、接口中方法均为抽象方法 5 接口中不能包含静态方法。
- 类不可以继承多个类
- 接口可以继承多个接口
- 类可以实现多个接口
- 接口抽象类区别
-
继承和聚合的区别在哪
- 继承:指的是一个类继承另外一个类的功能,并可以在此基础上进行拓展
- 聚合:体现的是整体与部分、拥有的关系,整体与部分是可以分离的,他们可以拥有自己完整的生命周期,部分可以属于多个整体对象,也可以为多个整体所共享,如 计算机 与CPU。
- IO模型有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。
- bio: 同步并阻塞io,服务器运用一个请求对应一个线程模式.客户端每有新的请求时,服务端都会开启新线程等待处理。
- nio: 同步非阻塞io, nio采用多路复用机制,服务端运用一个线程维护Selector调度者,客户端发送的请求连接到多路复用器上,其轮询到有I/O请求时,开启线程处理
- aio:异步非阻塞io,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成再通知服务器开启线程处理,依赖os版本
- reactor模型:处理多个输入源,将其事件采用多路复用分发响应的处理
- 反射的原理,反射创建类实例的三种方式是什么。
- 反射原理: java文件经过编译后生成.class文件,其在加载到jvm过程中,java可以通过反射获取所有的类信息,包括 构造方法,属性等。
- 反射创建实例三种方式:
- Class.forName(“类的路径”);
- 类.class
- 实例.getClass
- 反射中,Class.forName和ClassLoader.loadClass区别 。
- Class.forName 方法,内部实际调用方法 Class.forName(className,true,classloader),默认进行初始化,静态代码块会被初始化
- ClassLoader.loadClass 方法,内部实际调用 ClassLoader.loadClass(className,false);第二个参数表示目标对象是否进行连接,不进行连接意味着不进行一系列的初始化动作
- java类加载国城: 加载-》验证-》准备-》解析-》初始化-》使用-》卸载
- 描述动态代理的几种实现方式,分别说出相应的优缺点。
- 动态代理:利用反射和字节码,在运行期创建指定接口或类的实例对象,通过这种方式可以对代码进行无侵入式增强
- 两种方式:
- jdk 动态代理:利用Proxy类 一个接口InvocationHanlder.Proxy 所有动态类的父类,重写invocationHanlder 的invoke方法进行拓展
- cglib 动态代理: 基于ASM的字节码生成库,允许运行期动态修改和生成。通过Enhancer 指定要代理的目标对象,通过MethodInterceptor的intercept 方法进行增强
- 优缺点:
- jdk动态代理 java原生支持,不需要外部依赖,其有接口硬性要求
- cglib 无需接口 通过继承方式进行代理
- final的用途。
- 用final修饰的类不能被拓展,不能有子类
- 用final修饰的方法不能被替换或隐藏
-
写出三种单例模式实现 。
-
懒汉式(双重校验)
public class SingleTon { private volatile static SingleTon instance ; private SingleTon(){ } public static SingleTon getSingleTon(){ if (instance == null){ synchronized (SingleTon.class){ if (instance == null){ instance = new SingleTon(); } } } return instance; } }
-
饿汉式
public class SingleTon { private static SingleTon instance = new SingleTon(); private SingleTon(){ } public static SingleTon getSingleTon(){ return instance; } }
-
静态内部类:低延迟,线程安全
public class SingleTon { private static class innerSingleTon{ private final static SingleTon instance = new SingleTon(); } private SingleTon(){ } public static SingleTon getSingleTon(){ return innerSingleTon.instance; } }
-
-
如何在父类中为子类自动完成所有的hashcode和equals实现?这么做有何优劣。
- 父类重写Object的hashcode 和 equals 实现,如Object默认equals是比较地址值,父类可重写为值相等等情况,子类继承后可自动引用父类定义。
-
请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设计中的作用。
-
个人理解java修饰符设计是用来封装的,类与类之间有多重关系,通过修饰符可以更好的做控制
-
本类内 本包类 其他包子类 其他包 public + + + + protected + + + default + + private +
-
-
深拷贝和浅拷贝区别。
- 浅拷贝:增加指向已存在的内存地址
- 深拷贝:增加一个指针并且申请新的内存,使用该指针指向新的内存
-
数组和链表数据结构描述,各自的时间复杂度
- 数组:元素在内存中连续存放,通过头结点和偏移量可迅速定位元素,查询的时间复杂度O(1)
- 链表:链表中元素在内存中不是连续的,通过元素指针进行连接,查询需要从根节点开始遍历查找,时间复杂度O(N)
-
error和exception的区别,CheckedException,RuntimeException的区别。
- error和exception 继承与Throwable, error 是java程序运行中不可预料的异常情况,异常发生后,会直接导致JVM不可处理或者不可恢复的状况,OutOfMemoryError等。Exception是java程序运行中可预料的异常情况
- Exception 分为检查性异常 和 运行时异常。CheckedException 是需要编译器校验的,通过throws 或者try/catch 语句块来出利益异常。RuntimeException 在定义时不需要声明抛出异常或者捕获异常,通常是由于程序逻辑引起的。
-
请列出5个运行时异常。
- ClassCastException 类转换异常
- IndexOutOfBoundsException 数组越界
- NullPointerException 空指针
- ArrayStoreException 数组存储异常
- ArithmeticException 算数异常
-
在自己的代码中,如果创建一个java.lang.String类,这个类是否可以被类加载器加载?为什么。
- 这个类不会被加载。jvm加载类提供了多种加载器。其关系为:自定义类加载器->应用程序类加载器->拓展类加载器->启动类加载器. 他们的层次关系被称为 双亲委派模型。如果一个类加载器收到了类加载的请求,首先不会尝试加载,而是委派父类加载器。通过该种方式来保护java已经提供的类,其中就有String类。
-
说一说你对java.lang.Object对象中hashCode和equals方法的理解。在什么场景下需要重写
- Object中equals 是对两个对象的地址值比较,Object中hashcode() 是一个本地方法,其实现是根据本地环境相关的,其可以返回对象的hash地址值。
- 集合中,例如map中存自定义对象时,需要重写这两个方法,因为map在查询比较过程中,是先判断hashcode 值,再判断equals. hashcode不同,equals一定不同。
-
这样的a.hashcode() 有什么用,与a.equals(b)有什么关系。
- hashCode 的作用是获取哈希码,确定对象在哈希表中的位置
- equals 相等 hashCode 一定相等。
- hashCode 不相等,equals 也不等
- hashCode 相等 , equals 不一定相等
-
在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。
- 泛型用来参数化类型,未有泛型前,对象想下转型需要显示强制类型转换,这种转换在编译器是不检查的,容易在运行期触发转换异常,引入泛型后,编译时会检查参数类型,所有转换都是隐式的,并提高代码重用率。
-
Java中的HashSet内部是如何工作的。
- HashSet 内部采用HashMap 来实现,由于map 需要 key 和 value,所以HashSet 中 所有的key都有个默认value. hashSet 不允许有重复的key , 其只允许有一个null对象
-
什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。
- java序列化 是指把java对象转换为字节序列的过程。java反序列化是指把字节序列恢复为java对象的过程。
- 网络通讯时传输的是字节序列,所以需要进行java序列化
- 实现Serializable接口的类对象即可序列化,会生成个 serialVersionUID的版本号,用于校验一致性
- 声明为static 和transient 类型的成员变量不能被序列化
-
java8的新特性。
- Lambda表达式和函数式接口
- 接口的默认方法
- 方法引用
- 重复注解
- 增加新的工具类
- Optional 用于解决空指针异常
- Streams
- Base64
- JVM 使用 元空间 代替 持久代