1.面向对象基本特征

  1. 封装:

隐藏属性和方法,对数据的访问只提供对外公开的接口。对对象内部数据提供了不同级别的保护

修饰符 范围
private 只对当前类暴露
protected 只对当前类、子类和当前类所在包类暴露
public 对当前类、子类和当前类所在包类以及其他包类暴露
  1. 继承

子类继承父类方法以及属性,使得子类具有父类相同的行为(一般不建议子类重写父类方法)

  1. 多态

对于接口的同一个方法,不同实现类具有不同的表现形式。所以建议接口参数定义为接口(这样有利于实现各种设计模式)

2.+=与+的区别

  1. short s1 = 1; s1 = s1 + 1;
  2. 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.编写多线程程序的实现方式

  1. 继承thread类(Thread类其实也是实现了Runable接口)
  2. 实现Runnable接口(无异常抛出、异常在内部处理,无返回值)
  3. 实现Callable接口(异常抛出、异常在外部处理、有返回值)

    21.线程的join方法用途

    相当于当前线程执行到对应语句,会开始执行join线程的方法。join线程的方法执行完之后,才会继续执行当前线程剩下的语句

    22.Thread的start()和run()方法的区别

    run方法执行当前线程,不会新建一个线程执行
    start方法新建一个线程,线程初始处于Ready状态每当获取到Cpu时间片之后,进入到Running状态,开始run()方法

    23.Java虚拟机线程的状态

    线程在某一个时间点只能处于一个状态,同时这些状态只针对于Java虚拟机而言,不代表任何操作系统的状态

  4. NEW:线程被创建还未启动

  5. READY:线程进入就绪状态,获取到CPU时间片开始运行run()
  6. RUNNING:线程进入运行状态
  7. BLOCKED:线程进入同步锁/同步代码块,等待同步锁的状态
  8. WAITING:线程进入等待状态,释放同步锁,需等待其他线程调用notify()/notifyAll()方法进行唤醒
  9. TIMED_WAITING:线程进入等待状态,但是具有时效性,不会释放同步锁,过期时间到了之后线程自动恢复
  10. TERMINATED:线程执行完毕并已经退出

    24.Synchronized和Lock的区别

  11. Lock是一个接口,synchronized是关键字

  12. Lock方法异常不会释放锁,需要通过finally代码块调用unlock()释放锁,synchronized发生异常会自动释放锁
  13. Lock更加灵活,有响应中断,超时时间等。synchronized需要线程一直等待直到获取到锁
  14. 性能近些年已经趋近于相同,建议优先使用synchronized

    25.Synchronized加锁场景的作用范围

  15. 作用于非静态方法(public synchronized void method())

非静态方法需要由对象调用,因此每一个对象实例有一个锁

  1. 作用于静态方法(public static synchronized void method())

静态方法需要由类调用,静态方法存于方法区,线程共享,因此所有调用该方法的线程只有一把锁

  1. 作用于.class(synchronized(Person.class){})

相当于给该类加了一把锁,因此所有调用该方法的线程只有一把锁

  1. 作用于对象(synchronized(this))

每一个对象实例有一把锁

  1. 作用于静态变量

静态变量存于方法区,线程共享,因此全局只有一把锁

26.死锁发生的条件

  1. 互斥:该资源只能被一个线程占有,在此期间其他线程无法获取该资源
  2. 占有但不释放:线程获取到当前资源还需获取其他资源,但其他资源暂时无法获取,并且该线程也不释放当前资源
  3. 强制:无法强制剥夺该线程释放当前资源
  4. 环路:多个线程因为占有但不释放形成环路等待,发生死锁

    27.预防死锁

  5. 打破互斥:该资源可被多个线程占用并操作。一般不会打破互斥原则

  6. 打破占有但不释放:线程获取其他资源时必须释放当前资源
  7. 打破强制:可强制剥夺线程持有的当前资源
  8. 打破环路:资源有序分配。

    28.线程池的优势

  9. 减少频繁创建销毁线程造成的消耗

  10. 提高响应速度。不用等待线程创建好之后才去执行任务,可直接调用当前处于空闲状态的线程
  11. 提高可控性,统一管理、监控线程

    29.线程池的参数

    threadFactory(线程工厂):用于创建线程
    corePoolSize(核心线程数):线程池中的核心线程数,当线程池中的线程数少于核心线程数,会创建新的线程,即使其他线程处于空闲状态
    maximumPoolSize(最大线程数):线程池中允许创建的最大线程数量
    workQueue(队列):用于保存任务并将任务交给工作线程的阻塞队列
    handler(拒绝策略):线程池处于非RUNNING状态,线程池中线程数量已达到最大线程数且阻塞队列已满
    keepAliveTime(保活时间):当线程池线程数量多于核心线程数时,如果线程空闲时间超过最大保活时间,销毁该线程,直到线程数等于核心线程数

    30.JAVA内存结构

  12. 程序计数器:线程私有

  13. 虚拟机栈:线程私有
  14. 本地方法栈:线程私有
  15. 堆:线程共享,用于存于引用数据类型对象实际值
  16. 方法区:线程共享,用于存储静态变量、常量、已加载的类信息
  17. 运行时常量池:线程共享,用于存放编译期生成的各种字面量和符号引用。例如字符串在创建时存于StringPool

    31.双亲委派机制

    每一个类加载器收到类加载指令后都会将指令传递给父类加载器,每一层加载器都是如此,只有当父类加载器确认无法加载当前类之后,才会交给子类去加载

    32.类加载器的类型

  18. 启动类加载器(Bootstrap ClassLoader

  19. 扩展类加载器(Extension ClassLoader
  20. 应用程序类加载器(Application ClassLoader
  21. 自定义类加载器

    33.类加载过程

    类加载的过程包括:加载、验证、准备、解析、初始化,其中验证、准备、解析统称为连接。
    加载:通过一个类的全限定名来获取定义此类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
    验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
    准备:为静态变量分配内存并设置静态变量初始值,这里所说的初始值“通常情况”下是数据类型的零值。
    解析:将常量池内的符号引用替换为直接引用。
    初始化:到了初始化阶段,才真正开始执行类中定义的 Java 初始化程序代码。主要是静态变量赋值动作和静态语句块(static{})中的语句。

    34.JAVA垃圾回收机制

  22. 如何判定对象是否需要被回收

引用计数法(存在循环引用因此不适用于JAVA虚拟机)/可达性分析法
可达性分析法:根据GcRoot作为根节点向下搜索,所有不可达的对象都被标记为需回收对象

  1. GcRoot包括哪些

虚拟机栈、本地方法栈中引用的对象。方法区中的静态变量,方法区中的常量

  1. 垃圾回收的算法 | 算法 | 不足 | | —- | —- | | 标记清除法 | 效率低,会产生内存碎片 | | 标记整理法 | 效率低 | | 复制法 | 浪费内存空间 | | 分代收集法 | 新生代:复制法 老年代:标记清除/标记整理法(新生代或来老年代都是对于堆内存而言的) |

  2. 新生代与老年代

JAVA基础相关 - 图1

35.类加载顺序

  1. 初始化的顺序是先静态对象,然后是非静态对象。
  2. 当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器 ->子类非静态变量(非静态代码块)->子类构造器 。

    36.synchronized原理

    synchronized 底层使用了3个双向链表来存放被阻塞的线程,3个双向链表分别是:_cxq(Contention queue)、_EntryList(EntryList)、_WaitSet(WaitSet)。
    当线程获取锁失败进入阻塞后,首先会被加入到 _cxq 链表,_cxq 链表的节点会被进一步转移到 _EntryList 链表。
    当持有锁的线程释放锁后,_EntryList 链表头结点的线程会被唤醒,该线程称为 OnDeck 线程,然后该线程会尝试抢占锁。
    而当我们调用 wait() 时,线程会被放入 _WaitSet,直到调用了 notify()/notifyAll() 后,线程才被重新放入 _cxq 或 _EntryList,默认放入 _cxq 链表头部。
    JAVA基础相关 - 图2