java 基础
- 1. JAVA 中的几种基本数据类型是什么,各自占用多少字节
- 2. 类的实例化顺序
- 3. String, Stringbuffer, StringBuilder 的区别
- 4. ArrayList 和 LinkedList
- 5. Map的各种实现以及区别
- 6. 抽象类和接口的区别
- 7. IO 模型
- 8. 反射的原理以及3中实现方式,Class.forName 和 ClassLoader 区别
- 9. 动态代理的几种实现方式,相应的优缺点
- 10. final 的用途
- 11. 写出三种单例模式实现
- 12. 在父类中为子类自动完成所有的 hashcode 和 equals 实现,这么做有何优劣。
- 13. 请结合 OO 设计理念,谈谈访问修饰符 public、 private、 protected、 default 在应用设计中的作用。
- 14.深拷贝和浅拷贝区别。
- 15.error和exception的区别,CheckedException,RuntimeException的区别,以及常用的异常
- 16. 在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?
- 17. 在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。
- 18. 有没可能 2个不相等的对象有同hashcode。
- 19. Java 中的 HashSet 内部是如何工作的。
- 20. 什么是序列化,怎么序列化,为什么序列化,反序列
- 21. 方法的重写规则
- 22.并行 并发的区别
- 23.开发设计原则(solid)
- 24.java对象头
- 25 linux 服务器CPU较高时排查
- 二 JVM 知识
一.java 基础
1. JAVA 中的几种基本数据类型是什么,各自占用多少字节
int 4字节,double 8字节,boolean (理论上占用1bit,1/8字节,实际处理按1byte处理),byte 1字节,char 2字节, short 2字节,long 8字节,float 4字节
2. 类的实例化顺序
a.父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
b.子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
c.父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行
d.父类构造方法
e.子类实例成员和实例初始化块,按在代码中出现的顺序依次执行
f.子类构造方法
注:内部类(不论是静态内部类还是非静态内部类)都是在第一次使用时才会被加载
3. String, Stringbuffer, StringBuilder 的区别
答:String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。 Stringbuffer 安全不过要慢,StringBuilder不安全但是快
4. ArrayList 和 LinkedList
ArrayList(内部数组实现,查询快,初始为10,每次扩容为原来的1.5倍)和LinkedList(内部链表实现,新增 删除快,无初始化 大小,也没有扩容机制)是对List接口的不同数据结构的实现。它们都是线程不安全的
5. Map的各种实现以及区别
map类型 | 默认容量 | 扩容方式 | key和value是否允许null | 线程安全 | 是否有序 | 备注 |
---|---|---|---|---|---|---|
HashMap | 默认大小16 | 超过长度的0.75倍就进行扩容(*2) | 允许null key 和null value | 非线程安全 | 无序 | |
HashTable | 默认大小11 | 超过长度的0.75倍就进行扩容(*2+1) | 不允许key或者value 为 null | 线程安全(在各个方法上添加了synchronize关键字) | 无序 | |
ConcurrentHashMap | 默认大小16 | 超过长度的0.75倍就进行扩容(*2) | 不允许key或者value 为空 | 线程安全(segment段锁,jdk1.8使用CAS+Synchronize) | 无序 | 因为Java的源码贡献者在进行大量实验发现,hash碰撞发生8次的概率已经降低到了0.00000006,几乎为不可能事件,如果真的碰撞发生了8次,那么这个时候说明由于元素本身和hash函数的原因,此时的链表性能已经已经很差了,操作的hash碰撞的可能性非常大了,后序还会继续发生hash碰撞。在这种极端的情况才会把链表转换为红黑树,提高性能的,大部分情况下hashMap还是使用链表 红黑树转链表的阈值为6,中间有个差值7可以防止链表和树之间的频繁转换 |
TreeMap | 红黑树实现无容量限制 | 红黑树实现无容量限制 | 默认key不可为null,value可以为null 若实现compare接口,key根据各自实现可能可以为空 | 线程不安全 | 有序,key从小到大排序 | |
LinkedHashMap | 同HashMap | 同HashMap | 同HashMap | 同HashMap | 有序 | |
weakHashMap | 同HashMap | 同HashMap | 同HashMap | 同HashMap | 无序 | |
SynchronizedMap | 同被包装Map | 同被包装Map | 同被包装Map | 单个简单操作线程安全,若复杂的方法,仍存在线程不安全 | 同被包装Map |
weakHashMap 弱键
1.新建WeakHashMap,将“键值对”添加到WeakHashMap中。实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表
2.当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中
3.当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
6. 抽象类和接口的区别
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口成员变量默认为public static final,必须赋初值,不能被修改 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。(java8以后接口可以有default和static方法,所以可以运行main方法) |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 | 如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
7. IO 模型
阻塞 | 非阻塞 | |
---|---|---|
同步 | 调用者持续等待直到结果返回,而被调用者在处理另一个请求,不接受当前请求 | 调用者持续等待直到结果返回,被调用者在处理另一个请求时,接受当前请求,任意请求结束后自行返回 |
异步 | 调用者发起请求后不等待直接执行后续逻辑。被调用者在处理另一个请求,不接受当前请求 | 调用者发起请求后不等待直接执行后续逻辑。被调用者在处理一个请求时,接受其他请求,任意请求结束后自行返回、 |
BIO | 伪异步IO | NIO | AIO | |
---|---|---|---|---|
客户端数目 :IO线程 | 1:1 | m:n | m:1 | m:0 |
IO模型 | 同步阻塞IO | 同步阻塞IO | 同步非阻塞IO | 异步非阻塞IO |
吞吐量 | 低 | 中 | 高 | 高 |
编程复杂度 | 简单 | 简单 | 非常复杂(ServerSocketChannel,Selector) | 复杂(AsynchronousServerSocketChannel,CompletionHandler) |
BIO NIO java demo
https://blog.csdn.net/bingxuesiyang/article/details/89679294 AIO java
demo https://www.jianshu.com/p/2d33ad3e89a6
8. 反射的原理以及3中实现方式,Class.forName 和 ClassLoader 区别
原理:Class,Constructor,Field,Method;基于将.java 文件编译为.class文件,抽象成的4个类。
1 Class clazz1=Person.class;
2 Class clazz2=new Person().getClass();
3 Class clazz3=Class.forName(“com.test.domain.Person”);
Class.forName 和 ClassLoader 区别:Class.forName加载类时将类进了初始化,而ClassLoader的loadClass并没有对类进行初始化,只是把类加载到了虚拟机中。即Class.forName 包含了 ClassLoader 的步骤。
9. 动态代理的几种实现方式,相应的优缺点
优点 | 缺点 | 实现所需类 | |
---|---|---|---|
jdk | java原声支持的、不需要任何外部依赖 | 它只能基于接口进行代理(因为它已经继承了proxy了,java不支持多继承) | |
cglib | 无论目标对象没有没实现接口都可以代理 | 无法处理final的情况(final修饰的方法不能被覆写)注:springaop 的cglib 不支持同一个类中方法相互调用 |
10. final 的用途
final 修饰在变量上,说明这个变量的值不能修改.
final 修饰在方法上,说明这个方法在继承后不能覆盖. final
修饰在类上,说明这个类不能被继承了.
11. 写出三种单例模式实现
优点 | 缺点 | 示例 | |
---|---|---|---|
饿汉式 | 饿汉式在类创建的同时就实例化一个静态对象出来,在第一次调用时速度也会更快,线程安全 | 类加载较慢,可能浪费不必要的空间 | ![]() |
懒汉式(线程不安全) | 速度快 | 无法在多线程中使用 | ![]() |
懒汉式(线程安全) | 可用于多线程 | 效率不高 | ![]() |
双重加锁(懒汉式升级版本) | 可用于多线程(一定要加volatile修饰,否则由于JVM本身原因会存在问题) | 加了锁比上一版本效率高但是仍不是很高 | ![]() |
内部类 | 兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性 | 需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。 | ![]() |
枚举(类似内部类的进阶版) | 避免序列化,反射等进行创建新实例(Enum实现了反射报异常的逻辑),线程安全 | ![]() |
12. 在父类中为子类自动完成所有的 hashcode 和 equals 实现,这么做有何优劣。
有时父类的equals和hashcode不满足我们自己的要求,我们需要重新去重写。
13. 请结合 OO 设计理念,谈谈访问修饰符 public、 private、 protected、 default 在应用设计中的作用。
OO设计理念:封装、继承、多态
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。所以我们可以通过public、private、protected、default 来进行访问控制
关键字 | 类内部 | 本包 | 子类 | 外部包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
14.深拷贝和浅拷贝区别。
深拷贝复制原 对象属性,浅拷贝指向原对象(注浅拷贝,若数据类型为基础数据类型则也是进行复制)。
15.error和exception的区别,CheckedException,RuntimeException的区别,以及常用的异常
空指针异常(java.lang.nullpointerexception)
指定的类不存在(java.lang.ClassNotFoundException)
字符串转换为数字异常(java.lang.NumberFormatException)
数组下标越界异常(java.lang.IndexOutOfBoundsException)
方法的参数错误(java.lang.IllegalArgumentException)
数据类型转换异常(java.lang.ClassCastException)
方法不存在异常(java.lang.NoSuchMethodException)
16. 在自己的代码中,如果创建一个 java.lang.String 类,这个类是否可以被类加载器加载?
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是说当发现这个类没有的时候会先去让自己的父类去加载,父类没有再让儿子去加载,那么在这个例子中我们自己写的String应该是被Bootstrap ClassLoader加载了,所以App ClassLoader就不会再去加载我们写的String类了,导致我们写的String类是没有被加载的。
如果一定需要 可以重写loadClass 打破双亲委派(要注意object是所有类的父类,加载时尽量不要使用自定义的加载类),如果重写的是java.包下的则还要重写至
17. 在jdk1.5中,引入了泛型,泛型的存在是用来解决什么问题。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
18. 有没可能 2个不相等的对象有同hashcode。
有可能,最简单的方法,百分百实现的方式就是重写hascode();
19. Java 中的 HashSet 内部是如何工作的。
HashSet 的内部采用 HashMap来实现。由于 Map 需要 key 和 value,所以HashSet中所有 key的都有一个默认 value。类似于 HashMap,HashSet 不允许重复的 key,只允许有一个null key,意思就是 HashSet 中只允许存储一个 null 对象。
20. 什么是序列化,怎么序列化,为什么序列化,反序列
序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化
序列化是为了解决在对对象流进行读写操作时所引发的问题。 transient 修饰的属性,是不会被序列化的
静态static的属性,他不序列化。 实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值
21. 方法的重写规则
参数列表必须完全与被重写方法的相同。 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5
及更早版本返回类型要一样,java7 及更高版本可以不同)。
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为
protected。 父类的成员方法只能被它的子类重写。 声明为 final 的方法不能被重写。 声明为 static
的方法不能被重写,但是能够被再次声明。 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final
的方法。 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
构造方法不能被重写。 如果不能继承一个方法,则不能重写这个方法
22.并行 并发的区别
- 并发(Concurrent)
在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几个时间区间之间来回切换,由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多个应用程序同时在进行
- 并行(Parallel)
当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。其实决定并行的因素不是CPU的数量,而是CPU的核心数量,比如一个CPU多个核也可以并行。
23.开发设计原则(solid)
单一职责原则(Single Responsibility Principle, SRP)
一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因
开闭原则(Open-Closed Principle, OCP)
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
里氏代换原则(Liskov Substitution Principle, LSP)
所有引用基类(父类)的地方必须能透明地使用其子类的对象
接口隔离原则(Interface Segregation Principle, ISP)
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
依赖倒转原则(Dependency Inversion Principle, DIP)
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
迪米特法则(Law of Demeter, LoD)
一个软件实体应当尽可能少地与其他实体发生相互作用
合成复用原则(Composite Reuse Principle, CRP)
尽量使用对象组合,而不是继承来达到复用的目的
24.java对象头
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
ObjectDTO a = new ObjectDTO(); System.out.println(GraphLayout.parseInstance(a).toPrintable()); a.hashCode(); System.out.println(ClassLayout.parseInstance(a).toPrintable());
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
- hash: 对象的哈希码
- age: 对象的分代年龄
- biased_lock : 偏向锁标识位
- lock: 锁状态标识位
- JavaThread* : 持有偏向锁的线程 ID
- epoch: 偏向时间戳
CMS free block和CMS promoted object 应该是CMS 垃圾收集器使用到的某几个状态个人猜测是空闲内存块以及待晋升对象。
最后2位(5时是3位)代表的状态
enum { locked_value = 0,
unlocked_value = 1,
monitor_value = 2,
marked_value = 3,
biased_lock_pattern = 5
};
是否偏向锁 | 锁状态标识位 | |||
---|---|---|---|---|
locked_value | 轻量锁 | 0 | - | 00 |
unlocked_value | 无锁 | 1 | - | 01 |
monitor_value | 重量级锁 | 2 | - | 10 |
marked_value | 不清楚 | 3 | - | 11 |
biased_lock_pattern | 偏向锁 | 4 | - | 01 |
JVM一般是这样使用锁和Mark Word的:
25 linux 服务器CPU较高时排查
- 1.根据ps 或者top命令查看CPU 占用较高的进程
pidxx=ps -aux --sort=-pcpu|grep -E 'geshui|admin|weblogic'|grep -v root
- 2.获取pid并查看具体的线程信息
pid=echo $pidxx|cut -d" " -f2
;tidxx=top -Hp $pid -b -n 1 | head -n 8|tail -n 1
- 3.获取线程号并转换成16进制
tid=echo $tidxx|cut -d" " -f1
;echo $tid;tid16=printf '%x' $tid
- 4.根据jstack获取详细的信息
jstack $pid|grep $tid16 -A 10
二 JVM 知识
https://blog.csdn.net/soulonlyhlh/article/details/100184986