说一下Java的特点
- 平台无关性:Java“编写一次,运行无处不在”。Java编译器将源代码编译成字节码,该字节码可以在任何安装了Java虚拟机的系统上运行。
- 面向对象:Java是一门严格的面向对象编程语言,几乎一切都是对象。面向对象编程特性使得代码更易于维护和重用,包括类、对象、继承、多态、抽象和封装。
- 内存管理:Java有自己的垃圾回收机制,自动管理内存和回收不再使用的对象。这样,开发者不需要手动管理内存,从而减少内存泄漏和其他内存相关的问题。
Java 的优势和劣势是什么?
优势:
- 跨平台
- 面向对象
- 强大的生态系统
- 自动垃圾回收机制
- 多线程支持
- 稳定性,企业级应用长期使用
劣势:
- 性能不如C++/Rust。
- 语法繁琐,比如样板代码多
- 内存消耗,JVM本身占内存,对于资源有限的环境可能不太友好
- 相比动态语言如Python,Java需要更多代码,编译过程也可能拖慢开发节奏
Java为什么是跨平台的?
Java能支持跨平台,主要依赖于JVM关系比较大。
JVM也是一个软件,不同的平台有不同的版本。我们编写的Java源码,编译后会生成一种.class文件,称为字节码文件。Java虚拟机就是负责将字节码文件翻译成特定平台下的机器码然后运行。
而这个过程中,我们编写的Java程序没有做任何改变,仅仅是通过JVM这一“中间层”,就能在不同平台上运行,真正实现了“一次编译,到处运行”的目的。
编译的结果不是生成机器码,而是生成字节码,字节码不能直接运行,必须通过JVM翻译成机器码才能运行。不同平台下编译生成的字节码是一样的,但是由JVM翻译成的机器码却不一样。
跨平台的是Java程序,不是JVM。JVM是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的JVM。
jvm,jdk,jre区别
JVM ⊆ JRE ⊆ JDK
- JVM是Java虚拟机,是Java程序运行的环境。它负责将Java字节码解释或编译成机器码,并执行程序。JVM提供了内存管理、垃圾回收、安全性等功能,使得Java程序具备跨平台性。
- JDK是Java开发工具包,是开发Java程序所需的工具集合。它包含了JVM、编译器(javac)、调试器(jdb)等开发工具,以及一系列的类库。JDK提供了开发、编译、调试和运行Java程序所需的全部工具和环境。
- JRE 是 Java 运行时环境,是 Java 程序运行所需的最小环境。它包含了 JVM 和一组 Java 类库,用于支持 Java 程序的执行。JRE 不包含开发工具,只提供 Java 程序运行所需的运行环境。
为什么 Java 解释和编译都有?
首先在 Java 经过编译之后生成字节码文件,接下来进入 JVM 中,就有两个步骤编译和解释。
- 编译性:Java 源代码首先被编译成字节码,JIT 会把编译过的机器码保存起来,以备下次使用。
- 解释性:JVM 中一个方法调用计数器,当累计计数大于一定值的时候,就使用 JIT 进行编译生成机器码文件。否则就是用解释器进行解释执行,然后字节码也是经过解释器进行解释运行的。
所以 Java 既是编译型也是解释性语言,默认采用的是解释器和编译器混合的模式。
jvm是什么
JVM是java虚拟机,主要工作是解释自己的指令集(即字节码)并映射到本地的CPU指令集和OS的系统调用。
JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的字节码,就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。
编译型语言和解释型语言的区别?
编译型语言和解释型语言的区别在于:
- 编译型语言:在程序执行之前,整个源代码会被编译成机器码或者字节码,生成可执行文件。执行时直接运行编译后的代码,速度快,但跨平台性较差。
- 解释型语言:在程序执行时,逐行解释执行源代码,不生成独立的可执行文件。通常由解释器动态解释并执行代码,跨平台性好,但执行速度相对较慢。
- 典型编译型语言如C、C++,解释型语言如Python、JavaScript。
Python和Java区别是什么?
- Java是一种已编译的编程语言,Java编译器将源代码编译为字节码,而字节码则由Java虚拟机执行
- python是一种解释语言,翻译时会在执行程序的同时进行翻译。
八种基本数据类型
| 数据类型 | 占用大小(字节) | 位数 | 取值范围 |
|---|---|---|---|
| byte | 1 | 8 | -27 到 27 - 1 |
| short | 2 | 16 | -215 到 215 - 1 |
| int | 4 | 32 | -231 到 231 - 1 |
| long | 8 | 64 | -263 到 263 - 1 |
| float | 4 | 32 | 1.4E-45 到 3.4E38 |
| double | 8 | 64 | 4.9E-324 到 1.8E308 |
| char | 2 | 16 | ‘\u0000’ 到 ‘\uffff’ |
| boolean | 无明确字节大小(理论上1位) | 无明确位数 | true 或 false |
int和long是多少位,多少字节的?
- int 类型是32位,占4个字节,int是有符号整数类型,其取值范围是从-2^31到2^31-1。
- long 类型是64位,占8个字节, long 类型也是有符号整数类型,它的取值范围是从-2^63到2^63-1,在处理较大的整数数值时,果 int 类型的取值范围不够,就需要使用 long 类型。
long和int可以互转吗?
可以的。由于 long 类型的范围比 int 类型大,因此将 int 转换为 long 是安全的,而将 long 转换为 int 可能会导致数据丢失或溢出。
- 将 int 转换为 long 可以通过直接赋值或强制类型转换来实现。
- 将 long 转换为 int 需要使用强制类型转换,但需要注意潜在的数据丢失或溢出问题。
在将 long 转换为 int 时,如果 longValue 的值超出了 int 类型的范围,转换结果将是截断后的低位部分。因此,在进行转换之前,建议先检查 longValue 的值是否在 int 类型的范围内,以避免数据丢失或溢出的问题。
数据类型转换方式你知道哪些?
- 自动类型转换(隐式转换):当目标类型的范围大于源类型时,Java会自动将源类型转换为目标类型,不需要显式的类型转换。
- 强制类型转换(显式转换):当目标类型的范围小于源类型时,需要使用强制类型转换将源类型转换为目标类型。这可能导致数据丢失或溢出。
- 字符串转换:Java提供了将字符串表示的数据转换为其他类型数据的方法。例如,将字符串转换为整型 int ,可以使用 Integer.parseInt() 方法;将字符串转换为浮点型 double ,可以使用 Double.parseDouble() 方法等。
- 数值之间的转换:Java 提供了一些数值类型之间的转换方法,如将整型转换为字符型、将字符型转换为整型等。这些转换方式可以通过类型的包装类来实现
类型互转会出现什么问题吗?
- 基本数据类型转换的问题
- 当把小范围数据类型赋值给大范围数据类型时,Java 会自动进行类型转换,这种转换一般是安全的。
- 大范围数据类型赋值给小范围数据类型时,会发生数据溢出或者精度损失的问题。
- 对象引用转换的问题
- 向上转型是自动进行的,而且是安全的。
- 向下转型需要手动进行,并且存在风险。如果父类对象实际上并不是目标子类的实例,在转型时就会抛出异常。
Java的对象在运行时会记录其真实类型,当进行向下转型时,Java会检查对象的实际类型是否与目标类型兼容。如果不兼容,就会抛出
ClassCastException。解决方式是需要使用instanceof检查:只有确认对象是目标子类的实例时才进行转型。
为什么用BigDecimal不用double?
double会出现精度丢失的问题,因为double执行的是二进制浮点运算,二进制有时不能准确表示一个小数——二进制只能表示成1/2n的和的组合,但0.1无法表示为这种形式,因此会出现精度误差。
比如Java中浮点数运算的输出会偏离预期(如0.05+0.01结果是0.060000000000000005),这在商品价格计算等场景中会造成严重问题(比如0.05+0.01的总和无法匹配0.06元的支付),尤其高并发场景下会导致下单、对账异常。
而BigDecimal是精确计算,所以涉及金钱的计算一般用BigDecimal。
这样使用BigDecimal可以确保十进制数值的精确计算,避免double的舍入误差。需要注意的是:创建BigDecimal对象时应使用字符串作为参数,而非直接用浮点数值,以防浮点精度丢失。
装箱和拆箱是什么?
装箱和拆箱是将基本数据类型和对应的包装类之间进行转换的过程。
自动装箱主要发生在两种情况:
- 赋值时:这是最常见的一种情况,在Java 1.5以前我们需要手动地进行转换才行,而现在所有的转换都是由编译器来完成。
- 方法调用时:当我们在方法调用时,我们可以传入原始数据值或者对象,同样编译器会帮我们进行转换。
自动装箱的弊端:
自动装箱有一个问题,那就是在一个循环中进行自动装箱操作,会创建多余的对象,会降低程序的性能并且加重了垃圾回收的工作量。
Integer相比int有什么优点?
int是Java中的原始数据类型,而Integer是int的包装类。
Integer和int的区别:
- 基本类型和引用类型:首先,int是一种基本数据类型,而Integer是一种引用类型。基本数据类型是Java中最基本的数据类型,它们是预定义的,不需要实例化就可以使用。而引用类型则需要通过实例化对象来使用。这意味着,使用int来存储一个整数时,不需要任何额外的内存分配,而使用Integer时,必须为对象分配内存。在性能方面,基本数据类型的操作通常比相应的引用类型快。
- 自动装箱和拆箱:其次,Integer作为int的包装类,它可以实现自动装箱和拆箱。自动装箱是指将基本类型转化为相应的包装类类型,而自动拆箱则是将包装类类型转化为相应的基本类型。
- 空指针异常:另外,int变量可以直接赋值为0,而Integer变量必须通过实例化对象来赋值。如果对一个未经初始化的Integer变量进行操作,就会出现空指针异常。这是因为它被赋予了null值,而null值是无法进行自动拆箱的。
那为什么还要保留int类型?
包装类是引用类型,对象的引用和对象本身是分开存储的,而对于基本类型数据,变量对应的内存块直接存储数据本身。
因此,基本类型数据在读写效率方面,要比包装类高效。除此之外,在64位JVM上,在开启引用压缩的情况下,一个Integer对象占用16个字节的内存空间,而一个int类型数据只占用4字节的内存空间,前者对空间的占用是后者的4倍。
也就是说,不管是读写效率,还是存储效率,基本类型都比包装类高效。
说一下integer的缓存
Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象。
默认情况下,这个范围是-128至127。当通过Integer.valueOf(int)方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象,会直接从内存中取出,不需要新建一个对象。