前言
回首来看2020年,真的是印象中过的最快的一年了,真的是时间过的飞快,还没反应过来年就夸完了,相信大家也已经开始上班了!俗话说新年新气象,马上就要到了一年之中最重要的金三银四,之前一直有粉丝要求我整理一些java岗的面试题,年前一直没时间,这次趁着元旦节给大家整理了一些一线互联网公司java岗面试题主要是Java基础+多线程+集合+JVM,满满的干货放在下面了!内容有点多,大家可以挑选自己需要的部分看!
Java 语⾔有哪些特点?
- 简单易学;
- ⾯向对象(封装,继承,多态);
- 平台⽆关性( Java 虚拟机实现平台⽆关性);
- 可靠性;
- 安全性;
- ⽀持多线程( C++ 语⾔没有内置的多线程机制,因此必须调⽤操作系统的多线程功能来进⾏多线程程序设计,⽽ Java 语⾔却提供了多线程⽀持);
⾯向对象和⾯向过程的区别
- ⾯向过程 :⾯向过程性能⽐⾯向对象⾼。 因为类调⽤时需要实例化,开销⽐⼤,⽐消耗资源,所以当性能是最重要的考量因素的时候,⽐如单⽚机、嵌⼊式开发、Linux/Unix 等⼀般采⽤⾯向过程开发。但是,⾯向过程没有⾯向对象易维护、易复⽤、易扩展。
- ⾯向对象 :⾯向对象易维护、易复⽤、易扩展。 因为⾯向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,⾯向对象性能⽐⾯向过程低。
什么是java虚拟机
- Java 虚拟机(JVM)是运⾏ Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),⽬的是使⽤相同的字节码,它们都会给出相同的结果。
什么是字节码?采⽤字节码的好处是什么?
- 在 Java 中,JVM 可以理解的代码就叫做 字节码 (即扩展名为 .class 的⽂件),它不⾯向任何特定的处理器,只⾯向虚拟机。Java 语⾔通过字节码的⽅式,在⼀定程度上解决了传统解释型语⾔执⾏效率低的问题,同时⼜保留了解释型语⾔可移植的特点。所以 Java 程序运⾏时⾼效,⽽且,由于字节码并不针对⼀种特定的机器,因此,Java 程序⽆须重新编译便可在多种不同操作系统的计算机上运⾏。
关于JVM和字节码
- Java 虚拟机(JVM)是运⾏ Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),⽬的是使⽤相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语⾔“⼀次编译,随处可以运⾏”的关键所在。
JDK 和 JRE
- JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。
- JRE 是 Java 运⾏时环境。它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的⼀些基础构件。但是,它不能⽤于创建新程序。
Oracle JDK 和 OpenJDK 的对⽐
- Oracle JDK ⼤概每 6 个⽉发⼀次主要版本,⽽ OpenJDK 版本⼤概每三个⽉发布⼀次。但这不是固定的,我觉得了解这个没啥⽤处。
- OpenJDK 是⼀个参考模型并且是完全开源的,⽽ Oracle JDK 是 OpenJDK 的⼀个实现,并不是完全开源的;
- Oracle JDK ⽐ OpenJDK 更稳定。OpenJDK 和 Oracle JDK 的代码⼏乎相同,但 Oracle JDK 有更多的类和⼀些错误修复。因此,如果您想开发企业/商业软件,我建议您选择 Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些⼈提到在使⽤ OpenJDK 可能会遇到了许多应⽤程序崩溃的问题,但是,只需切换到 Oracle JDK 就可以解决问题;
- 在响应性和 JVM 性能⽅⾯,Oracle JDK 与 OpenJDK 相⽐提供了更好的性能;
- Oracle JDK 不会为即将发布的版本提供⻓期⽀持,⽤户每次都必须通过更新到最新版本获得⽀持来获取最新版本;
- Oracle JDK 根据⼆进制代码许可协议获得许可,⽽ OpenJDK 根据 GPL v2 许可获得许可。
什么是 Java 程序的主类 应⽤程序和⼩程序的主类有何不同?
- ⼀个程序中可以有多个类,但只能有⼀个类是主类。在 Java 应⽤程序中,这个主类是指包含main()⽅法的类。⽽在 Java ⼩程序中,这个主类是⼀个继承⾃系统类 JApplet 或 Applet 的⼦类。应⽤程序的主类不⼀定要求是 public 类,但⼩程序的主类要求必须是 public 类。主类是 Java程序执⾏的⼊⼝点。
Java 应⽤程序与⼩程序之间有哪些差别?
- 简单说应⽤程序是从主线程启动(也就是 main() ⽅法)。applet ⼩程序没有 main() ⽅法,主要是嵌在浏览器⻚⾯上运⾏(调⽤ init() 或者 run() 来启动),嵌⼊浏览器这点跟 flash 的⼩游戏类似。
字符型常量和字符串常量的区别?
- 形式上: 字符常量是单引号引起的⼀个字符; 字符串常量是双引号引起的若⼲个字符
- 含义上: 字符常量相当于⼀个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表⼀个地址值(该字符串在内存中存放位置)
- 占内存⼤⼩ 字符常量只占 2 个字节; 字符串常量占若⼲个字节 (注意: char 在 Java 中占两个字节)
构造器 Constructor 是否可被 override?
- Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。
重载和重写的区别
- 重载就是同样的⼀个⽅法能够根据输⼊数据的不同,做出不同的处理
- 重写就是当⼦类继承⾃⽗类的相同⽅法,输⼊数据⼀样,但要做出有别于⽗类的响应时,你就要覆盖⽗类⽅法
说一下Java ⾯向对象编程三⼤特性: 封装 继承 多态
封装
- 封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法,如果属性不想被外界访问,我们⼤可不必提供⽅法给外界访问。但是如果⼀个类没有提供给外界访问的⽅法,那么这个类也没有什么意义了。
继承
- 继承是使⽤已存在的类的定义作为基础建⽴新类的技术,新类的定义可以增加新的数据或新的功能,也
可以⽤⽗类的功能,但不能选择性地继承⽗类。通过使⽤继承我们能够⾮常⽅便地复⽤以前的代码。
关于继承如下 3 点请记住:
- ⼦类拥有⽗类对象所有的属性和⽅法(包括私有属性和私有⽅法),但是⽗类中的私有属性和⽅
法⼦类是⽆法访问,只是拥有。 - ⼦类可以拥有⾃⼰属性和⽅法,即⼦类可以对⽗类进⾏扩展。
- ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法。(以后介绍)。
多态
- 所谓多态就是指程序中定义的引⽤变量所指向的具体类型和通过该引⽤变量发出的⽅法调⽤在编程时并不确定,⽽是在程序运⾏期间才确定,即⼀个引⽤变量到底会指向哪个类的实例对象,该引⽤变量发出的⽅法调⽤到底是哪个类中实现的⽅法,必须在由程序运⾏期间才能决定。在 Java 中有两种形式可以实现多态:继承(多个⼦类对同⼀⽅法的重写)和接⼝(实现接⼝并覆盖接⼝中同⼀⽅法)。
说一下⾃动装箱与拆箱
- 装箱:将基本类型⽤它们对应的引⽤类型包装起来;
- 拆箱:将包装类型转换为基本数据类型;
在⼀个静态⽅法内调⽤⼀个⾮静态成员为什么是⾮法的?
由于静态⽅法可以不通过对象进⾏调⽤,因此在静态⽅法⾥,不能调⽤其他⾮静态变量,也不可以访问⾮静态变量成员。
在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤
- Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。
import java 和 javax 有什么区别?
- 刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使⽤。然⽽随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java包确实太麻烦了,最终会破坏⼀堆现有的代码。因此,最终决定 javax 包将成为标准 API 的⼀部分。所以,实际上 java 和 javax 没有区别。这都是⼀个名字。
接⼝和抽象类的区别是什么?
- 接⼝的⽅法默认是 public,所有⽅法在接⼝中不能有实现(Java 8 开始接⼝⽅法可以有默认实现),⽽抽象类可以有⾮抽象的⽅法。
- 接⼝中除了 static、final 变量,不能有其他变量,⽽抽象类中则不⼀定。
- ⼀个类可以实现多个接⼝,但只能实现⼀个抽象类。接⼝⾃⼰本身可以通过 extends 关键字扩
展多个接⼝。 - 接⼝⽅法默认修饰符是 public,抽象⽅法可以有 public、protected 和 default 这些修饰符(抽象⽅法就是为了被重写所以不能使⽤ private 关键字修饰!)。
- 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,⽽接⼝是对⾏为的抽象,是⼀种⾏为的规范。
成员变量与局部变量的区别有哪些?
- 从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数;成
员变量可以被 public,private,static 等修饰符所修饰,⽽局部变量不能被访问控制修饰符及
static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。 - 从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属
于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局部
变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆内存对
象的引⽤或者是指向常量池中的地址。 - 从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部
变量随着⽅法的调⽤⽽⾃动消失。 - 成员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 final 修饰的
成员变量也必须显式地赋值),⽽局部变量则不会⾃动赋值。
创建⼀个对象⽤什么运算符?对象实体与对象引⽤有何不同?
- new 运算符,new 创建对象实例(对象实例在堆内存中),对象引⽤指向对象实例(对象引⽤存放在栈内存中)。⼀个对象引⽤可以指向 0 个或 1 个对象(⼀根绳⼦可以不系⽓球,也可以系⼀个⽓球);⼀个对象可以有 n 个引⽤指向它(可以⽤ n 条绳⼦系住⼀个⽓球)。
什么是⽅法的返回值?返回值在类的⽅法⾥的作⽤是什么?
- ⽅法的返回值是指我们获取到的某个⽅法体中的代码执⾏后产⽣的结果!(前提是该⽅法可能产⽣结果)。返回值的作⽤:接收出结果,使得它可以⽤于其他的操作!
⼀个类的构造⽅法的作⽤是什么? 若⼀个类没有声明构造⽅法,该程序能正确执⾏吗? 为什么?
- 主要作⽤是完成对类对象的初始化⼯作。可以执⾏。因为⼀个类即使没有声明构造⽅法也会有默认的不
带参数的构造⽅法。
构造⽅法有哪些特性?
- 名字与类名相同。
- 没有返回值,但不能⽤ void 声明构造函数。
- ⽣成类的对象时⾃动执⾏,⽆需调⽤。
静态⽅法和实例⽅法有何不同
- 在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。
⽽实例⽅法只有后⾯这种⽅式。也就是说,调⽤静态⽅法可以⽆需创建对象。 - 静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不允许
访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。
对象的相等与指向他们的引⽤相等,两者有什么不同?
- 对象的相等,⽐的是内存中存放的内容是否相等。⽽引⽤相等,⽐的是他们指向的内存地址是否相
等。
在调⽤⼦类构造⽅法之前会先调⽤⽗类没有参数的构造⽅法,其⽬的是?
- 帮助⼦类做初始化⼯作。
简述线程、程序、进程的基本概念。以及他们之间关系是什么?
线程与进程相似,但线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程。与进程不同的是同类的多个线程共享同⼀块内存空间和⼀组系统资源,所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作时,负担要⽐进程⼩得多,也正因为如此,线程也被称为轻量级进程。
程序是含有指令和数据的⽂件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。
进程是程序的⼀次执⾏过程,是系统运⾏程序的基本单位,因此进程是动态的。系统运⾏⼀个程序即是⼀个进程从创建,运⾏到消亡的过程。简单来说,⼀个进程就是⼀个执⾏中的程序,它在计算机中⼀个指令接着⼀个指令地执⾏着,同时,每个进程还占有某些系统资源如 CPU 时间,内存空间,⽂件,输⼊输出设备的使⽤权等等。换句话说,当程序在执⾏时,将会被操作系统载⼊内存中。 线程是进程划分成的更⼩的运⾏单位。线程和进程最⼤的不同在于基本上各进程是独⽴的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互影响。从另⼀⻆度来说,进程属于操作系统的范畴,主要是同⼀段时间内,可以同时执⾏⼀个以上的程序,⽽线程则是在同⼀程序内⼏乎同时执⾏⼀个以上的程序段。
关于 final 关键字的⼀些总结
- final 关键字主要⽤在三个地⽅:变量、⽅法、类。
- 对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
- 当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。
- 使⽤ final ⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;第⼆个原因是效率。在早期的 Java 实现版本中,会将 final ⽅法转为内嵌调⽤。但是如果⽅法过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升(现在的 Java 版本已经不需要使⽤ final⽅法进⾏这些优化了)。类中所有的 private ⽅法都隐式地指定为 final。
Java 中的异常处理
Java 异常类层次结构图
- 在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。Throwable: 有两个重要的⼦类:Exception(异常) 和 Error(错误) ,⼆者都是 Java 异常处理的重要⼦类,各⾃都包含⼤量⼦类。
- Error(错误):是程序⽆法处理的错误,表示运⾏应⽤程序中严重问题。⼤多数错误与代码编写者执⾏的操作⽆关,⽽表示代码运⾏时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运⾏错误(Virtual MachineError),当 JVM 不再有继续执⾏操作所需的内存资源时,将出现
- OutOfMemoryError。这些异常发⽣时,Java 虚拟机(JVM)⼀般会选择线程终⽌。这些错误表示故障发⽣于虚拟机⾃身、或者发⽣在虚拟机试图执⾏应⽤时,如 Java 虚拟机运⾏错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应⽤程序的控制和处理能⼒之 外,⽽且绝⼤多数是程序运⾏时不允许出现的状况。对于设计合理的应⽤程序来说,即使确实发⽣了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过 Error 的⼦类描述。
- Exception(异常):是程序本身可以处理的异常。Exception 类有⼀个重要的⼦类RuntimeExceptionRuntimeException 异常由 Java 虚拟机抛出。NullPointerException(要访问的变量没有引⽤任何对象时,抛出该异常)、ArithmeticException(算术运算异常,⼀个整数除以 0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
- 注意:异常和错误的区别:异常能被程序本身处理,错误是⽆法处理。
Throwable 类常⽤⽅法
- public string getMessage():返回异常发⽣时的简要描述
- public string toString():返回异常发⽣时的详细信息
- public string getLocalizedMessage():返回异常对象的本地化信息。使⽤ Throwable 的⼦类覆盖这个⽅法,可以⽣成本地化信息。如果⼦类没有覆盖该⽅法,则该⽅法返回的信息与getMessage()返回的结果相同
- public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息
异常处理总结
- try 块: ⽤于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟⼀个finally 块。
- catch 块: ⽤于处理 try 捕获到的异常。
- finally 块: ⽆论是否捕获或处理异常,finally 块⾥的语句都会被执⾏。当在 try 块或catch 块中遇到 return 语句时,finally 语句块将在⽅法返回之前被执⾏。
在以下 4 种特殊情况下,finally 块不会被执⾏:
- 在 finally 语句块第⼀⾏发⽣了异常。 因为在其他⾏,finally 块还是会得到执⾏
- 在前⾯的代码中⽤了 System.exit(int)已退出程序。 exit 是带参函数 ;若该语句在异常语句之后,finally 会执⾏
- 程序所在的线程死亡。
- 关闭 CPU。
Java 序列化中如果有些字段不想进⾏序列化,怎么办?
- 对于不想进⾏序列化的变量,使⽤ transient 关键字修饰。transient 关键字的作⽤是:阻⽌实例中那些⽤此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和⽅法。
获取⽤键盘输⼊常⽤的两种⽅法
⽅法 1:通过 Scanner
⽅法 2:通过 BufferedReader
讲一下Java 中 IO 流
Java 中 IO 流分为⼏种?
- 按照流的流向分,可以分为输⼊流和输出流;
- 按照操作单元划分,可以划分为字节流和字符流;
- 按照流的⻆⾊划分为节点流和处理流。
Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派⽣出来的。 - InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
按操作⽅式分类结构图:
按操作对象分类结构图:
既然有了字节流,为什么还要有字符流?
- 字符流是由 Java 虚拟机将字节转换得到的,问题就出在这个过程还算是⾮常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以, I/O 流就⼲脆提供了⼀个直接操作字符的接⼝,⽅便我们平时对字符进⾏流操作。如果⾳频⽂件、图⽚等媒体⽂件⽤字节流⽐好,如果涉及到字符的话使⽤字符流⽐好。
讲一下深拷⻉ vs 浅拷⻉
- 浅拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型进⾏引⽤传递般的拷⻉,此为浅拷⻉。
- 深拷⻉:对基本数据类型进⾏值传递,对引⽤数据类型,创建⼀个新的对象,并复制其内容,此
为深拷⻉。
说说List,Set,Map三者的区别?
- List(对付顺序的好帮⼿): List接⼝存储⼀组不唯⼀(可以有多个元素引⽤相同的对象),有序的对象
- Set(注重独⼀⽆⼆的性质): 不允许重复的集合。不会有多个元素引⽤相同的对象。
- Map(⽤Key来搜索的专家): 使⽤键值对存储。Map会维护与Key有关联的值。两个Key可以引⽤相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
HashMap 和 Hashtable 的区别
- 线程是否安全: HashMap 是⾮线程安全的,HashTable 是线程安全的;HashTable 内部的⽅法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使⽤ ConcurrentHashMap吧!);
- 效率: 因为线程安全的问题,HashMap 要⽐ HashTable 效率⾼⼀点。另外,HashTable 基本被淘汰,不要在代码中使⽤它;
- 对Null key 和Null value的⽀持: HashMap 中,null 可以作为键,这样的键只有⼀个,可以有⼀个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有⼀个 null,直接抛出 NullPointerException。
- 初始容量⼤⼩和每次扩充容量⼤⼩的不同 : ①创建时如果不指定容量初始值,Hashtable 默认的初始⼤⼩为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化⼤⼩为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为2的幂次⽅⼤⼩(HashMap 中的 tableSizeFor() ⽅法保证,下⾯给出了源代码)。也就是说 HashMap 总是使⽤2的幂作为哈希表的⼤⼩,后⾯会介绍到为什么是2的幂次⽅。
- 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了⼤的变化,当链表⻓度⼤于阈值(默认为8)时,将链表转化为红⿊树,以减少搜索时间。Hashtable 没有这样的机制。
什么是线程和进程?
何为进程?
- 进程是程序的⼀次执⾏过程,是系统运⾏程序的基本单位,因此进程是动态的。系统运⾏⼀个程序即是⼀个进程从创建,运⾏到消亡的过程。
- 在 Java 中,当我们启动 main 函数时其实就是启动了⼀个 JVM 的进程,⽽ main 函数所在的线程就是这个进程中的⼀个线程,也称主线程。
何为线程?
- 线程与进程相似,但线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程。与进程不同的是同类的多个线程共享进程的堆和⽅法区资源,但每个线程有⾃⼰的程序计数器、虚拟机栈和本地⽅法栈,所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作时,负担要⽐进程⼩得多,也正因为如此,线程也被称为轻量级进程。
为什么要使⽤多线程呢?
- 从计算机底层来说: 线程可以⽐作是轻量级的进程,是程序执⾏的最⼩单位,线程间的切换和调度的成本远远⼩于进程。另外,多核 CPU 时代意味着多个线程可以同时运⾏,这减少了线程上下⽂切换的开销。
- 从当代互联⽹发展趋势来说: 现在的系统动不动就要求百万级甚⾄千万级的并发量,⽽多线程并发编程正是开发⾼并发系统的基础,利⽤好多线程机制可以⼤⼤提⾼系统整体的并发能⼒以及性能
并发编程的三个重要特性
- 原⼦性 : ⼀个的操作或者多次操作,要么所有的操作全部都得到执⾏并且不会收到任何因素的⼲扰⽽中断,要么所有的操作都执⾏,要么都不执⾏。 synchronized 可以保证代码⽚段的原⼦性。
- 可⻅性 :当⼀个变量对共享变量进⾏了修改,那么另外的线程都是⽴即可以看到修改后的最新值。 volatile 关键字可以保证共享变量的可⻅性。
- 有序性 :代码在执⾏的过程中的先后顺序,Java 在编译器以及运⾏期间的优化,代码的执⾏顺序未必就是编写代码时候的顺序。 volatile 关键字可以禁⽌指令进⾏重排序优化。
简单的介绍⼀下强引⽤,软引⽤,弱引⽤,虚引⽤
强引⽤(StrongReference)
- 以前我们使⽤的⼤部分引⽤实际上都是强引⽤,这是使⽤最普遍的引⽤。如果⼀个对象具有强引⽤,那就类似于必不可少的⽣活⽤品,垃圾回收器绝不会回收它。当内存空 间不⾜,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终⽌,也不会靠随意回收具有强引⽤的对象来解决内存不⾜问题。
软引⽤(SoftReference)
- 如果⼀个对象只具有软引⽤,那就类似于可有可⽆的⽣活⽤品。如果内存空间⾜够,垃圾回收器就不会回收它,如果内存空间不⾜了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使⽤。软引⽤可⽤来实现内存敏感的⾼速缓存。软引⽤可以和⼀个引⽤队列(ReferenceQueue)联合使⽤,如果软引⽤所引⽤的对象被垃圾回收,JAVA虚拟机就会把这个软引⽤加⼊到与之关联的引⽤队列中。
弱引⽤(WeakReference)
- 如果⼀个对象只具有弱引⽤,那就类似于可有可⽆的⽣活⽤品。弱引⽤与软引⽤的区别在于:只具有弱引⽤的对象拥有更短暂的⽣命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,⼀旦发现了只具有弱引⽤的对象,不管当前内存空间⾜够与否,都会回收它的内存。不过,由于垃圾回收器是⼀个优先级很低的线程, 因此不⼀定会很快发现那些只具有弱引⽤的对象。弱引⽤可以和⼀个引⽤队列(ReferenceQueue)联合使⽤,如果弱引⽤所引⽤的对象被垃圾回收,Java虚拟机就会把这个弱引⽤加⼊到与之关联的引⽤队列中。
虚引⽤(PhantomReference)
- "虚引⽤"顾名思义,就是形同虚设,与其他⼏种引⽤都不同,虚引⽤并不会决定对象的⽣命周期。如果
⼀个对象仅持有虚引⽤,那么它就和没有任何引⽤⼀样,在任何时候都可能被垃圾回收。
总结
感谢你看到这里,文章有什么不足还请指正!由于时间关系暂时先整理了这么多,有什么不足的可以私信我,我都会跟进补上,希望这些能在马上到来的金三银四里面对你有帮助!
另外我还为大家准备了ava核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书一起免费分享给大家!
有需要的可以关注我的公众号:前程有光回复资料即可领取!觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!