基础概念与常识
Java语言有哪些特点
- 简单易学
- 面向对象(封装、继承、多态)
- 平台无关性(Java虚拟机实现平台无关性)
- 支持多线程
- 支持网络编程
- 可靠性
- 安全性
- 编译与解释并存
Write Once, Run Anywhere
目前市面上虚拟化技术已经非常成熟,通过Docker很容易实现跨平台。在我看来,Java强大的生态才是!
JVM vs JDK vs JRE
-
JVM
JVM是运行Java字节码的虚拟机。JVM有针对不同系统的特定实现(Windows、Linux、macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的JVM实现是Java语言“一次编译,随处可以运行”的关键所在。
JVM并不是只有一种!只要满足JVM规范,每个公司、组织或者个人都可以开发自己的专属JVM。
一些常见JVM:
- HotSpot VM
- J9 VM
- Zing VM
- JRockit VM
-
JDK 和 JRE
JDK:
-
Java Development kit
-
功能齐全的Java SDK
-
拥有JRE所拥有的一切,还有编译器(javac)和工具。它能够创建和编译程序
JRE:
- Java运行时环境。运行已编译Java程序所需的所有内容的集合,包括JVM、Java类库、Java命令和其他的一些基础构建。不能用于创建新程序。
使用:
- 如果你只是为了运行一下 Java 程序的话,那么你只需要安装 JRE 就可以了。如果你需要进行一些 Java 编程方面的工作,那么你就需要安装 JDK 了。但是,这不是绝对的。有时,即使您不打算在计算机上进行任何 Java 开发,仍然需要安装 JDK。例如,如果要使用 JSP 部署 Web 应用程序,那么从技术上讲,您只是在应用程序服务器中运行 Java 程序。那你为什么需要 JDK 呢?因为应用程序服务器会将 JSP 转换为 Java servlet,并且需要使用 JDK 来编译 servlet。
-
什么是字节码?采用字节码的好处是什么?
Java中,JVM可以理解的代码就叫做字节码(即扩展名为.class的文件),它不面向任何特定处理器,只面向虚拟机。
Java通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以,Java程序运行时相对来说还是高效的。而且,由于字节码并不针对一种特定的机器,因此,Java程序无须重新编译便可在多种不同操作系统的计算机上运行。
注意 .class->机器码 这一步:
这一步JVM类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对较慢。而且,有些方法和代码块是经常需要被调用的(热点代码),所以后面引进了JIT(just-in-time compliation)编译器。而JIT属于运行时编译,当JIT编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。这也解释了我们为什么经常会说Java是编译与解释共存的语言。
HotSpot采用了惰性评估(Lazy Evaluation):
根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况搜集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。
JDK 9 引入了一种新的编译模式AOT(Ahead of Time Compilation) , 它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销(冷启动)。JDK支持分层编译和AOT协作使用。
为什么不全部使用AOT呢?
AOT可以提前编译节省启动实践,那为什么不全部使用这种编译方式呢?
这与Java语言的动态特性有千丝万缕的联系。举个例子,CGLIB动态代理使用的是ASM技术,而这种技术大致原理是运行时直接在内存中生成并加载修改后的字节码文件也就是.class文件,如果全部使用AOT提前编译,也就不能使用ASM技术了。为了支持类似的动态特性,所以选择使用JIT即时编译器。
动态语言与静态语言(答案来自ChatGpt3.5)
-
动态语言与静态语言的区别:
动态语言和静态语言是编程语言的两种基本类型,它们在编译时和运行时的处理方式上有所不同。
动态语言是指在运行时才确定变量的数据类型,程序在运行时才解析变量的类型。这意味着开发人员可以更快地编写代码,因为不需要在编写代码时进行数据类型声明。一些常见的动态语言包括Python,Ruby,PHP等。
静态语言是指在编译时就已经确定变量的数据类型,程序在编译时会检查变量的类型。这意味着开发人员必须在编写代码时显式地声明数据类型。静态语言的优点是可以提高代码的可读性和可维护性,并且在编译时就可以发现类型错误。一些常见的静态语言包括Java,C++,C#等。
总之,动态语言和静态语言的主要区别在于变量类型在程序运行时是否确定。
-
那么为什么说Java具有动态特性呢
虽然Java通常被认为是一种静态语言,但是它也具有一些动态特性。这些特性包括:
- 反射:Java中的反射机制允许程序在运行时动态地获取类的信息,并调用其方法或构造函数。这使得Java程序可以根据需要在运行时动态地加载和使用类,而不是在编译时固定地使用类。
- 动态代理:Java中的动态代理机制允许程序在运行时创建代理对象,这些代理对象可以拦截方法调用并执行自定义逻辑。这使得Java程序可以在运行时动态地添加功能和修改行为。
- 动态绑定:Java中的动态绑定机制允许程序在运行时根据对象的实际类型调用方法。这使得Java程序可以在运行时动态地扩展类和实现多态。
- 动态加载:Java中的类加载机制允许程序在运行时动态地加载和卸载类。这使得Java程序可以根据需要加载和卸载类,而不是在编译时固定地使用类。
总之,虽然Java通常被认为是一种静态语言,但它也具有一些动态特性,这些特性使得Java程序可以在运行时动态地加载和使用类,以及在运行时动态地添加功能和修改行为。
为什么说Java语言"编译与解释并存"?
这是因为Java语言既具有编译型语言的特征,也具有解释型语言的特征。因为Java程序要经过先编译,后解释两个步骤,由Java编写的程序需要先经过编译步骤,生成字节码(.class文件),这种字节码必须由Java解释器来解释执行。
补充:
我们可以将高级编程语言按照程序的执行方式分为两种:
- 编译型 :编译型语言 会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
- 解释型 :解释型语言会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
即时编译技术:缩小了两种语言间效率的差距。这种技术混合了编译语言与解释型语言的优点,它像编译语言一样,先把程序源代码编译成字节码。到执行期,再将字节码直译,之后执行。Java和LLVM是这种技术的代表产物。
Oracle JDK vs OpenJDK
- 是否开源: OpenJDK是,Oracle JDK不是
- 是否免费: OpenJDK完全免费,Oracle JDK局部免费
- 功能性:Java11之后,功能基本一致
- 稳定性: 差不多
- 协议: Oracle JDK使用BCL/OTN协议获得许可,而OpenJDK根据GPL v2协议获得许可
Oracle JDK 和 OpenJDK 如何选择?
建议选择 OpenJDK 或者基于 OpenJDK 的发行版,比如 AWS 的 Amazon Corretto,阿里巴巴的 Alibaba Dragonwell。
Java和C++的区别?
都oop,都支持封装、继承和多态
-
Java 不提供指针来直接访问内存,程序内存更加安全
-
Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
-
Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
-
C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)。
-
......
基本语法
注释有哪几种形式?
标识符和关键字的区别是什么?
标识符是为变量、方法等取的名字
关键字是已经被Java语言赋予特殊含义的标识符,只能用于特定的地方。
注意 :虽然 true, false, 和 null 看起来像关键字但实际上他们是字面值,同时你也不可以作为标识符来使用。
自增自减运算符
移位运算符
如果移位的位数超过数值所占有的位数会怎么样?
答: 求余后再进行左移/右移操作。
continue、break和return的区别是什么?
变量
- 成员变量与局部变量的区别?
- 语法形式
- 存储方式
- 生存时间
- 默认值:成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被final修饰的成员变量也必须显示地赋值),局部变量则不会自动赋值。
- 静态变量有什么作用?
- 字符型常量和字符串常量的区别?
- 形式
- 含义
- 占内存大小(注意: char在Java中占两个字节)
方法
-
什么是方法的返回值?方法有哪几种类型?
-
静态方法为什么不能调用非静态成员?
- 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
- 在类的非静态成员不存在的时候静态方法就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。
-
静态方法和实例方法有何不同?
- 调用方式
- 访问类成员是否存在限制
-
重载和重写有什么区别?
重载:overload
重写:override
如果父类方法访问修饰符为private/final/static,则子类就不能重写该方法
重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。
- 什么是可变长参数?
public static void method1(String... args) {...}
public static void method2(String arg1, String... args){...}
可变参数只能作为函数的最后一个参数
遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢?
答:优先匹配固定参数的方法,因为固定参数的方法匹配度更高。
Java的可变参数编译后实际会被转换成一个数组,我们看编译后生成的class文件就可以看出来了
如何查看java编译后生成的class文件的内容
基本数据类型
-
Java中的几种基本数据类型了解么
Java中有8种基本数据类型: byte short、int、long、float、double、char、boolean
Java的每种基本类型所占存储空间的大小不会像其他大多数语言那样随机器硬件架构的变化而变化。这种所占存储空间大小的不变性是Java程序比用其他大多数语言编写的程序更具有可移植性的原因之一。
注意: Java里使用long类型的数据一定要在数值后面加上L,否则将作为整型解析。
-
基本类型和包装类型的区别?
-
包装类型的缓存机制了解吗?
Java基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。
Byte、Short、Integer、Long这4种包装类默认创建了数值[-128, 127]的相应类型的缓存数据,Character创建了数值在[0, 127]范围的缓存数据,Boolean直接返回True或者False
如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
两种浮点数类型的包装类Float、Double并没有实现缓存机制
下面我们看一个问题。下面的代码的输出结果是true还是false呢?
Integer i1 = 40; // 等价于 Integer i1 = Integer.valueOf(40) 自动装箱 Integer i2 = new Integer(40); System.out.println(i1 == i2);Integer i1=40这一行代码会发生装箱,也就是说这行代码等价于Integer i1=Integer.valueOf(40)。因此,i1直接使用的是缓存中的对象。而Integer i2 = new Integer(40)会直接创建新的对象。因此,答案是
false。记住:所有整型包装类对象之间值的比较,全部使用equals方法比较
-
自动装箱与拆箱了解吗?原理是什么?
举例:
Integer i = 10; // 自动装箱,等价于 Integer i = Integer.valueOf(10) int n = i;// 自动拆箱,等价于 int n = i.intValue()注意:频繁拆装箱的话,会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。
-
为什么浮点数运算的时候会有精度丢失的风险?
这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。
-
如何解决浮点数运算的精度丢失问题?
BigDemical可以实现对浮点数的运算,不会造成精度丢失
-
超过long整型的数据应该如何表示?
BigInteger内部使用int[]数组来存储任意大小的整型数据。
BigInteger运算的效率会相对较低。