【JavaGuide面试总结】Java基础篇·上
- 1.JVM vs JDK vs JRE
- 2.Java 和 C++ 的区别?
- 3.Java 程序从源代码到运行的过程
- 4.为什么说 Java 语言“编译与解释并存”?
- 5.说说default关键字的几个用法
- 6.静态变量有什么作用?
- 7.字符型常量和字符串常量的区别?
- 8.成员变量与局部变量的区别?
- 9.静态方法为什么不能调用非静态成员?
- 10.静态方法和实例方法有何不同?
1.JVM vs JDK vs JRE
- Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。
- JDK 是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
- JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
JDK > JRE > JVM🍕
2.Java 和 C++ 的区别?
Java 和 C++ 都是面向对象的语言,都支持封装、继承和多态
- Java 不提供指针来直接访问内存,程序内存更加安全
- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
- Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
- C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)
3.Java 程序从源代码到运行的过程
我们需要格外注意的是 .class(字节码)->机器码
这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。
JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT,它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。但是,AOT 编译器的编译质量是肯定比不上 JIT 编译器的。🍔
4.为什么说 Java 语言“编译与解释并存”?
我们可以将高级编程语言按照程序的执行方式分为两种:
- 编译型 :编译型语言会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有
C、C++、Go、Rust
等等。 - 解释型 :解释型语言会通过解释器一句一句的将代码解释为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有
Python、JavaScript、PHP
等等。
答案:这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(
.class
文件),这种字节码必须由 Java 解释器来解释执行。🍟
5.说说default关键字的几个用法
1.在程序控制中,当在 switch
中匹配不到任何情况时,可以使用 default
来编写默认匹配的情况。
2.在访问控制中,如果一个方法前没有任何修饰符,则默认会有一个修饰符 default
,但是这个修饰符加上了就会报错。
3.Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。我们只需在方法名前面加个 default
关键字即可实现默认方法。
为什么要有接口的默认方法呢?
之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类(这是一个庞大的工程!)所以之后发展了一种抽象类的概念,方法直接继承一个抽象类,而不和接口进行直接的联系,当进行业务扩充的时候,直接在抽象类里面进行扩充,这极大的简省了代码量,使整个程序更加的简洁,但是,我们说,这还远远不够!🌭
我们定义一个接口,其中包含了普通方法和默认方法:
public interface Book {
/**
* 这是普通的接口方法
*/
public void read();
/**
* 这是接口的默认方法,实现类不必实现这个方法
*/
default void create() {
System.out.println("创建图书模块");
}
}
接口的实现类,不需要重写接口的默认方法:
public class JavaBook implements Book{
/**
* 这个重写是必须的
*/
@Override
public void read() {
System.out.println("关于Java的书籍");
}
}
在接口中提供的 default
方法,如果要想使用,那么就必须通过接口实例化对象完成操作,对于 default
方法来讲必须要通过实例化对象才可以调用:
public class Main {
public static void main(String[] args) {
JavaBook javaBook = new JavaBook();
javaBook.read(); // 关于Java的书籍
javaBook.create(); // 创建图书模块
}
}
为了进一步的简化的操作,所以在接口中也提供有static
方法,这个方法可以由接口名称直接调用:
/**
* 接口的静态方法,这个方法可以由接口名称直接调用
* @return JavaBook实现类实例
*/
static Book getInstance() {
return new JavaBook();
}
public class Main {
public static void main(String[] args) {
Book book = Book.getInstance();
book.read(); // 关于Java的书籍
book.create(); // 创建图书模块
}
}
6.静态变量有什么作用?
静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。
通常情况下,静态变量会被 final
关键字修饰成为常量。
7.字符型常量和字符串常量的区别?
- 形式 : 字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
- 含义 : 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。
- 占内存大小 : 字符常量只占 2 个字节; 字符串常量占若干个字节。
8.成员变量与局部变量的区别?
语法形式 :成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public
,private
,static
等修饰符所修饰,而局部变量不能被访问控制修饰符及 static
所修饰;但是,成员变量和局部变量都能被 final
所修饰。
public class Main {
// 成员变量
private final int age = 18;
public static void main(String[] args) {
// 局部变量
final int now = 2023;
}
}
存储方式 :如果成员变量是使用 static
修饰的,那么这个成员变量是属于类的,如果没有使用 static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
生存时间 :成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
默认值 :成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值,而局部变量则不会自动赋值。
public class Main {
// 成员变量,int变量的默认值为0
public static int age;
public static void main(String[] args) {
// 局部变量,不会自动赋默认值,访问会出现一个错误
int now;
System.out.println(age); // 0
System.out.println(now); // java: 可能尚未初始化变量now
}
}
9.静态方法为什么不能调用非静态成员?
- 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
- 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。
public class Main {
// 非静态成员变量
public int age;
/**
* 静态方法
* @param args args
*/
public static void main(String[] args) {
System.out.println(age); // java: 无法从静态上下文中引用非静态 变量 age
}
}
10.静态方法和实例方法有何不同?
调用方式:
在外部调用静态方法时,可以使用 类名.方法名
的方式,也可以使用 对象.方法名
的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象 。
不过,需要注意的是一般不建议使用
对象.方法名
的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。🍿
public class Main {
public static void hotDog() {
System.out.println("我是静态方法");
}
public void redMi() {
System.out.println("我是实例方法");
}
public static void main(String[] args) {
Main.hotDog(); // 类名.静态方法名调用 - 我是静态方法
Main main = new Main();
main.redMi(); // 对象.方法名调用 - 我是实例方法
}
}
访问类成员是否存在限制:
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。