- 1.面向对象基本特征
- 2.+=与+的区别
- 3.Integer缓存区问题
- 4.最有效的方法计算2*8
- 5.&和&&的区别
- 6.Java基础数据类型
- 7.String相关
- 8.= 和 equals的差别
- 9.hashCode相同,是否一定equal
- 10.深拷贝与浅拷贝
- 11.构造器是否可以被重写
- 12.Java中的参数传递
- 13.Java中静态变量与属性变量的区别
- 14.重载与重写的区别
- 15.抽象类和接口的区别
- 16.Error和Exception的区别
- 17.Final关键字
- 18.JDK1.8新特性
- 19.wait()与sleep()的区别
- 20.编写多线程程序的实现方式
- 21.线程的join方法用途
- 22.Thread的start()和run()方法的区别
- 23.Java虚拟机线程的状态
- 24.Synchronized和Lock的区别
- 25.Synchronized加锁场景的作用范围
- 26.死锁发生的条件
- 27.预防死锁
- 28.线程池的优势
- 29.线程池的参数
- 30.JAVA内存结构
- 31.双亲委派机制
- 32.类加载器的类型
- 33.类加载过程
- 34.JAVA垃圾回收机制
- 35.类加载顺序
- 36.synchronized原理
1.面向对象基本特征
- 封装:
隐藏属性和方法,对数据的访问只提供对外公开的接口。对对象内部数据提供了不同级别的保护
| 修饰符 | 范围 |
|---|---|
| private | 只对当前类暴露 |
| protected | 只对当前类、子类和当前类所在包类暴露 |
| public | 对当前类、子类和当前类所在包类以及其他包类暴露 |
- 继承
子类继承父类方法以及属性,使得子类具有父类相同的行为(一般不建议子类重写父类方法)
- 多态
对于接口的同一个方法,不同实现类具有不同的表现形式。所以建议接口参数定义为接口(这样有利于实现各种设计模式)
2.+=与+的区别
short s1 = 1; s1 = s1 + 1;short s1 = 1; s1 += 1
s1 = s1 + 1编译期报错。short + int 会提示类型不兼容
s1 += 1 无异常。s1 += 1 可以理解为 s1 = (short)(s1 + 1),因此可以编译成功
3.Integer缓存区问题
Integer中引入了IntegerCache来缓存一定范围的值。IntegerCache缓存的范围:-128 ~ 127
在这个范围内的Integer调用=的结果为true,equals为true
不在这个范围内的Integer调用=的结果为false,equals为true
4.最有效的方法计算2*8
2<<<3
2的2进制为00010,向左(高位)移动3位变为10000,转化为10进制=16
5.&和&&的区别
&&:逻辑与运算符。
a&b当a、b都为true时,条件才成立。同时具备短路性,即当a为false时条件直接不成立,不用判定b
&:逻辑与运算符、按位与运算符。
a&b当a、b都为true时,条件才成立。不具备短路性,即当a为false时还需判定b是否为false才可判定条件是否成立
6.Java基础数据类型
共8个:byte、short、int、long、float、double、boolean、char
7.String相关
String类使用final关键字修饰的类,不可继承
| 类 | 线程安全 | 特点 |
|---|---|---|
| String | 否 | 创建后值不可被修改,每次+操作都创建新的String对象 |
| StringBuilder | 否 | 创建后值可以被修改,效率较高,线程不安全 |
| StringBuffer | 是 | 创建后值可以被修改,线程安全 |
字符串类型在创建时,存在一个StringPool的概念,StringPool中不存在重复字符串,StringPool也存在于堆中
因此String s = new String(“abc”);执行后,会先判定StringPool中是否含有”abc”,没有的话会先在StringPool中创建。同时new String也会在堆中创建一个abc对象。栈中存在引用s指向创建的abc对象
8.= 和 equals的差别
= 用于基础数据类型的判定,用于比较两个数据的值是否相同
equals用于引用数据类型的判定,用于比较两个引用数据类型具体指向堆中对象的值是否相同
引用数据类型=的话,只判定两个栈引用的内存地址是否相同,因此引用数据类型不用=作为判定标准
9.hashCode相同,是否一定equal
如果两个值equal,那么hashCode一定相等
反之如果hashCode相等,不一定equal,这就是hash冲突的由来
10.深拷贝与浅拷贝
| 拷贝类型 | 基本数据类型 | 引用数据类型 |
|---|---|---|
| 深拷贝 | 在栈中复制一个值 | 堆中开辟新的内存地址复制对象,并在栈中创建一个新的引用指向新的堆内存地址 |
| 浅拷贝 | 在栈中复制一个值 | 在栈中创建一个新的引用指向原堆内存地址 |
11.构造器是否可以被重写
构造器不可以被重写。但是可以重载。因此一个类中一般存在多种构造器(无参、多个参数)
12.Java中的参数传递
Java中只有值传递。对于引用类型参数,传递的值是这个对象的引用
13.Java中静态变量与属性变量的区别
静态变量存在于方法区中,属性变量存在于堆中。静态变量在类加载时就已经创建,属性变量在创建该类的对象时才被创建
14.重载与重写的区别
重载:一个类内部可创建多个重名方法。参数类型不同,参数数量不同都可,但是返回类型不同不可
重写:子类重写父类方法。参数类型不可改变,返回值可以改,但必须是父类返回值的派生类。根据设计模式来说一般不建议子类重写父类方法。
15.抽象类和接口的区别
接口定义一组行为,交给实现类去进行实现。接口本身没有任何实现逻辑。接口可以多实现。
抽象类将共性的行为提取出来自己实现,剩下的一些差异定义为抽象方法交给继承类去实现。抽象类只能单继承
16.Error和Exception的区别
Error为系统级异常和程序不必处理的异常,例如内存溢出。意味着异常可恢复但程序无法处理
Exception为程序中需要捕获和处理的异常。是一种逻辑上的异常。如果程序正常运行不会出现的异常
17.Final关键字
| 修饰对象 | 作用 |
|---|---|
| 修饰类 | 类不可被继承 |
| 修饰方法 | 方法不可被子类重写 |
| 修饰变量 | 变量在初始时必须给定初始值,且后续只有对该变量读取权限,无修改权限。对于对象,意味着无法修改该对象的引用,但可以修改该对象的属性 |
18.JDK1.8新特性
lambda表达式、StreamApi、方法引用、日期Api、Optional类
19.wait()与sleep()的区别
| 方法 | 来源 | 同步锁影响 | 使用范围 | 恢复方式 |
|---|---|---|---|---|
| wait() | Object | 当前线程释放同步锁 | 只能在同步控制方法或同步代码块里使用,不然会抛出异常 | 到时许等待其他线程调用notify()/notifyAll()才可恢复 |
| sleep() | Thread | 不会改变同步锁行为 | 可以在任意地方使用 | 到时继续执行 |
20.编写多线程程序的实现方式
- 继承thread类(Thread类其实也是实现了Runable接口)
- 实现Runnable接口(无异常抛出、异常在内部处理,无返回值)
实现Callable接口(异常抛出、异常在外部处理、有返回值)
21.线程的join方法用途
相当于当前线程执行到对应语句,会开始执行join线程的方法。join线程的方法执行完之后,才会继续执行当前线程剩下的语句
22.Thread的start()和run()方法的区别
run方法执行当前线程,不会新建一个线程执行
start方法新建一个线程,线程初始处于Ready状态每当获取到Cpu时间片之后,进入到Running状态,开始run()方法23.Java虚拟机线程的状态
线程在某一个时间点只能处于一个状态,同时这些状态只针对于Java虚拟机而言,不代表任何操作系统的状态
NEW:线程被创建还未启动
- READY:线程进入就绪状态,获取到CPU时间片开始运行run()
- RUNNING:线程进入运行状态
- BLOCKED:线程进入同步锁/同步代码块,等待同步锁的状态
- WAITING:线程进入等待状态,释放同步锁,需等待其他线程调用notify()/notifyAll()方法进行唤醒
- TIMED_WAITING:线程进入等待状态,但是具有时效性,不会释放同步锁,过期时间到了之后线程自动恢复
-
24.Synchronized和Lock的区别
Lock是一个接口,synchronized是关键字
- Lock方法异常不会释放锁,需要通过finally代码块调用unlock()释放锁,synchronized发生异常会自动释放锁
- Lock更加灵活,有响应中断,超时时间等。synchronized需要线程一直等待直到获取到锁
性能近些年已经趋近于相同,建议优先使用synchronized
25.Synchronized加锁场景的作用范围
作用于非静态方法(public synchronized void method())
非静态方法需要由对象调用,因此每一个对象实例有一个锁
- 作用于静态方法(public static synchronized void method())
静态方法需要由类调用,静态方法存于方法区,线程共享,因此所有调用该方法的线程只有一把锁
- 作用于.class(synchronized(Person.class){})
相当于给该类加了一把锁,因此所有调用该方法的线程只有一把锁
- 作用于对象(synchronized(this))
每一个对象实例有一把锁
- 作用于静态变量
26.死锁发生的条件
- 互斥:该资源只能被一个线程占有,在此期间其他线程无法获取该资源
- 占有但不释放:线程获取到当前资源还需获取其他资源,但其他资源暂时无法获取,并且该线程也不释放当前资源
- 强制:无法强制剥夺该线程释放当前资源
-
27.预防死锁
打破互斥:该资源可被多个线程占用并操作。一般不会打破互斥原则
- 打破占有但不释放:线程获取其他资源时必须释放当前资源
- 打破强制:可强制剥夺线程持有的当前资源
-
28.线程池的优势
减少频繁创建销毁线程造成的消耗
- 提高响应速度。不用等待线程创建好之后才去执行任务,可直接调用当前处于空闲状态的线程
-
29.线程池的参数
threadFactory(线程工厂):用于创建线程
corePoolSize(核心线程数):线程池中的核心线程数,当线程池中的线程数少于核心线程数,会创建新的线程,即使其他线程处于空闲状态
maximumPoolSize(最大线程数):线程池中允许创建的最大线程数量
workQueue(队列):用于保存任务并将任务交给工作线程的阻塞队列
handler(拒绝策略):线程池处于非RUNNING状态,线程池中线程数量已达到最大线程数且阻塞队列已满
keepAliveTime(保活时间):当线程池线程数量多于核心线程数时,如果线程空闲时间超过最大保活时间,销毁该线程,直到线程数等于核心线程数30.JAVA内存结构
程序计数器:线程私有
- 虚拟机栈:线程私有
- 本地方法栈:线程私有
- 堆:线程共享,用于存于引用数据类型对象实际值
- 方法区:线程共享,用于存储静态变量、常量、已加载的类信息
运行时常量池:线程共享,用于存放编译期生成的各种字面量和符号引用。例如字符串在创建时存于StringPool
31.双亲委派机制
每一个类加载器收到类加载指令后都会将指令传递给父类加载器,每一层加载器都是如此,只有当父类加载器确认无法加载当前类之后,才会交给子类去加载
32.类加载器的类型
启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extension ClassLoader)
- 应用程序类加载器(Application ClassLoader)
-
33.类加载过程
类加载的过程包括:加载、验证、准备、解析、初始化,其中验证、准备、解析统称为连接。
加载:通过一个类的全限定名来获取定义此类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备:为静态变量分配内存并设置静态变量初始值,这里所说的初始值“通常情况”下是数据类型的零值。
解析:将常量池内的符号引用替换为直接引用。
初始化:到了初始化阶段,才真正开始执行类中定义的 Java 初始化程序代码。主要是静态变量赋值动作和静态语句块(static{})中的语句。34.JAVA垃圾回收机制
如何判定对象是否需要被回收
引用计数法(存在循环引用因此不适用于JAVA虚拟机)/可达性分析法
可达性分析法:根据GcRoot作为根节点向下搜索,所有不可达的对象都被标记为需回收对象
- GcRoot包括哪些
虚拟机栈、本地方法栈中引用的对象。方法区中的静态变量,方法区中的常量
垃圾回收的算法 | 算法 | 不足 | | —- | —- | | 标记清除法 | 效率低,会产生内存碎片 | | 标记整理法 | 效率低 | | 复制法 | 浪费内存空间 | | 分代收集法 | 新生代:复制法 老年代:标记清除/标记整理法(新生代或来老年代都是对于堆内存而言的) |
新生代与老年代
35.类加载顺序
- 初始化的顺序是先静态对象,然后是非静态对象。
- 当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器 ->子类非静态变量(非静态代码块)->子类构造器 。
36.synchronized原理
synchronized 底层使用了3个双向链表来存放被阻塞的线程,3个双向链表分别是:_cxq(Contention queue)、_EntryList(EntryList)、_WaitSet(WaitSet)。
当线程获取锁失败进入阻塞后,首先会被加入到 _cxq 链表,_cxq 链表的节点会被进一步转移到 _EntryList 链表。
当持有锁的线程释放锁后,_EntryList 链表头结点的线程会被唤醒,该线程称为 OnDeck 线程,然后该线程会尝试抢占锁。
而当我们调用 wait() 时,线程会被放入 _WaitSet,直到调用了 notify()/notifyAll() 后,线程才被重新放入 _cxq 或 _EntryList,默认放入 _cxq 链表头部。
