1.JDK、JRE、JVM有什么区别?
-
JDK(Java Development Kit):是 Java 开发工具包,它提供了 Java 开发所需的开发工具、编译器和运行环境等多种组件。JJRE,还有一些开发者所需的额外组件(如编译器、调试器、文档生成工具等),可以用来开发和编译 Java 应用程序。JDK 对于开发者来说是必备的。
-
JRE(Java Runtime Environment):是 Java 运行环境,它为 Java 应用程序提供了运行环境。JRE 包括了Java虚拟机(JVM)、Java API类库和其他一些供运行Java程序所需的组件。在没有开发Java应用时,只需要安装 JRE 即可。
-
JVM(Java Virtual Machine):是 Java 虚拟机,它是一种运行在操作系统上的虚拟计算机,能够提供独立于硬件、操作系统的 Java 运行环境。JVM 是 Java 程序的运行平台,可以把 Java 代码编译成 JVM 所能执行的字节码,然后在 JVM 上运行。JVM 是 Java 跨平台的关键,它能够为不同的操作系统提供相同的 Java 运行环境。
JDK、JRE、JVM 代表了 Java 开发和运行环境中不同的层次,开发者需要使用 JDK 进行 Java 应用程序的开发和编译,用户需要安装 JRE 来运行 Java 应用程序,而 JVM 则是 Java 程序的运行环境,它运行的是 Java 字节码文件
2.请你介绍一下Java中的序列化和反序列化
首先,序列化的核心目的是解决多个JVM进程之间的对象传输问题,也就是说,如何把当前JVM进程里面的一个对象,传递到另外一个JVM进程里面。
序列化,就是把内存里面的对象转化为字节流,以便用来实现存储或者传输。
反序列化,就是根据获取到的对象的字节流,解析里面保存的对象描述信息和状态等重新构建一个新的对象。需要注意的是,反序列化得到的是一个与原始对象状态一致的新对象,与原始对象并不是同一个对象实例。
3.String、StringBuilder和StringBuffer的区别?
(1)String是不可变的,一旦创建,就无法修改它的值。任何对String对象的进行修改、拼接、替换操作实际上都会创造一个新的string对象,这会增加系统内存的开销。
(2)StringBuilder和StringBuffer都是可变的字符串类,它们可以进行字符串的增删改查操作。区别在于StringBuilder是非线程安全的,而StringBuffer是线程安全的。因为StringBuilder没有进行线程同步,所以在多线程环境中可以造成竞争条件,导致结果不可预测,而StringBuffer采用了线程同步机制,但是损失了一定的性能。
(3)加分项 StringBuilder和StringBuffer的toString()方法实现不同:StringBuilder的toString()返回的是原始字符序列的副本,而StringBuffer的toString()方法则是创建一个新的String对象。
4.StringBuffer采用了什么来实现线程安全?原理是什么?
StringBuffer采用了同步(synchronized)来实现线程安全。StringBuffer类中,几乎每个对字符串进行修改的方法都使用了synchronized关键字,这样就能够确保同一时间只有一个线程可以访问该对象。
当多个线程同时访问同一个StringBuffer实例时,每个线程会尝试获取锁,并且只有一个线程会获得锁,进而能够对StringBuffer对象进行修改。其他线程则需要等到已获得锁的线程释放锁之后才能访问。
虽然同步可以保证线程安全,但它也带来了一些开销,例如锁定和解锁操作及内部状态的维护。因此,在单线程环境下或者不需要线程安全的情况下,应该使用StringBuilder,而不是StringBuffer。
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
5.synchronized的原理是什么?
当一个线程进入synchronized块或方法时,它将会尝试获取锁。如果锁已经被其他线程获得,则该线程会被阻塞,直到获取到锁为止。然后,线程执行synchronized块或方法中的代码,执行完毕后释放锁,并将锁让给其他等待锁的线程。
这种机制可以确保同一时间只有一个线程可以执行synchronized块或方法中的代码,也就是说,这段代码具有原子性,不会出现数据竞争的情况。
需要注意的是,虽然synchronized可以确保线程安全,但使用不当会带来一些开销,例如锁定和解锁操作及内部状态的维护。因此,在实际应用中,应该避免过度使用synchronized。
在Java中,synchronized使用的是对象锁(也称为监视器锁)。当一个线程想要执行一个被synchronized方法或代码块所包围的区域时,它必须先获得与该方法或代码块关联的对象锁。如果另一个线程已经持有了该对象锁,则正在等待的线程将会被阻塞,直到该锁被释放。
这种基于对象锁的机制可以确保同一时间只有一个线程可以访问对象的同步代码,并且所有其他线程必须等待。
需要注意的是,每个对象都有一个相关的锁,因此不同对象之间的锁是互相独立的。在多线程编程中,不同的对象应该使用不同的锁来避免发生竞争和死锁问题。同时,尽可能减少同步代码块中的代码量以提高程序性能。
6.Java的8中基本数据类型?
6种数字类型:byte,short,int,long,float,double
1种字符类型:char
1种布尔型:boolean
7.基本类型和包装类型的区别?
(1)用途:除了定义一些常量和局部变量以外,我们在其他地方,比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可以用于泛型,而基本类型不可以。
(2)存储方式:基本数据类型的局部变量存在Java虚拟机的栈中。包装类型属于对象,几乎所有的对象都存放在堆中。
(3)占用空间:基本数据类型占用的空间一般都小于包装类型。
(4)默认值:基本数据类型基本都有默认值并且不是null,包装类型的不赋值就是null。
(5)比较方式:对于基本类型来说,==比较的是值,对于包装类型来说,==比较的是对象的内存地址。所有整型包装类型之间的比较,都使用equals()方法。
8.自动装箱与拆箱?原理是什么?
装箱:指的是把基本数据类型用它们对应的引用类型包装起来
拆箱:指的是把包装类型转化为基本数据类型
原理:装箱实际上就是调用了包装类的valueOf()方法,拆箱其实就是用了xxxValue()方法。
9.== 与 equals()区别?
(1)== 用来比较两个对象的内存地址是否相同,equals()是用来比较两个对象的内容是否相同。
(2)== 适用于基本数据类型和包装数据类型的比较,而equals()只适用于包装数据类型,因为基本数据类型没有内容的概念,只有值。
大部分类都有自己重写过的equals()方法,例如对象就是调用的Object的equals(),里面的逻辑就是==,但是例如String类型,它的equals()的实现久相对比较复杂。
10.Integer a = 1,Integer b = 1,它们 == 和 equals()的值分别是多少?如果是Integer a = 128,Integer b = 128呢?
(1)== 与 equals()的结果都是true。对于整数类型(byte,short,int,long),在-128到127之间的值,会被缓存起来重复使用,也就是说,当使用自动装箱的方式创建值在-128到127之间的整数时,系统会把他们缓存起来反复使用。换句话说,Integer a = 1和Integer b = 1都是自动装箱,表面上看是创建了两个对象,但是如果使用==来比较,实际上它们会被拆箱成int再比较,会比较他们的值是否相等。
(2)== 的结果是false,equals()的结果是true。缓存机制针对的是-128到127之间的整数,如果自动装箱的值大于127,那么就不会被缓存,== 就会比较两个对象之间的地址,结果自然就是false。
11.hashCode() 有什么用?两个对象的 hashCode() 相同,则 equals() 也一定为 true吗?
hashCode()可以返回对象的哈希码值,这个哈希码值是一个整数,作用是支持基于哈希表的数据结构,如HashMap、HashSet、Hashtable等。
在Java中,哈希表是一种将键与值相关联的数据结构,它可以快速基于键查找值。
当使用哈希表存储对象时,需要为对象生成一个哈希码值,这个值通常是根据对象的内容计算而来。如果对象相等(equals()方法返回true),那么它们的哈希码值应该相同;反之,如果对象不等,则它们的哈希码值可能相同也可能不同,但通常情况下还是希望它们的哈希码值不同,以提高哈希表的效率。
因此,hashCode() 方法的作用是为了支持基于哈希表的数据结构,对于元素的查找、添加、删除等操作都很重要。在使用哈希表的时候,Java会自动使用对象的hashCode()方法生成哈希码值,并且在哈希表中通过哈希码值来确定数组的索引,从而快速查找或添加元素,从而提高了程序的性能。
需要注意的是,hashCode() 方法并不能保证对象的唯一性,因为哈希表中可能会存在哈希冲突的情况,即不同的对象计算出来的哈希码值相同。因此,当使用自定义对象作为键值时,需要正确重写equals()和hashCode()方法,才能保证哈希表中键值的唯一性和正确性。
对于第二个问题,答案是否定的。
两个对象的hashCode() 相同只能说明它们所在的类重写了hashCode() 方法,使得相同的输入值可以计算出相同的结果。而equals() 方法用于检测两个对象是否具有相同的内容,所以hashCode()返回值相同的两个对象其实状态可以是不同的。但是,如果两个对象的equals() 方法返回true,那么它们的 hashCode() 必须相等,以满足哈希表的性能和效率要求。
12.final在java中有什么用?
(1)final修饰的类叫做最终类,该类不能被继承
(2)final修饰的方法不能被重写,从而保证方法的行为不会被修改
(3)final修饰的变量叫做常量,常量必须初始化,初始化之后的值不能被修改
(4)被final修饰的方法,JVM会尝试将其内联,以提高运行效率
(5)被final修饰的常量,在编译阶段会存入常量池中
13.普通类和抽象类有什么区别?
(1)抽象类不能被实例化,只能作为其他类的基类,子类必须实现抽象类的所有抽象方法才能被实例化。普通类则没有这个限制。
(2)抽象类可以包含抽象方法,这些方法没有具体的实现,需要在子类中实现。普通类只能包含具体的方法实现。
(3)抽象类不能被final修饰,因为抽象类是需要被继承的。普通类则可以被final修饰。
总结:抽象类是为了提供一个基础框架而存在的,而普通类通常是为了提供具体的功能实现而存在的。抽象类更强调对方法和属性的抽象,而普通类则更注重具体实现。
14.Java中有哪些流?它们有什么区别?
Java中的流(Stream)是处理输入输出任务的一种抽象方式,可以将数据的读取和存储从不同的物理设备、不同的操作系统中独立出来,并统一进行处理。Java中的流可以分为输入流和输出流两类,每一类中又分为字节流和字符流两种。
-
字节流:字节流的抽象类是InputStream(输入流)和OutputStream(输出流),它们分别针对字节数据的读取和存储进行了抽象。字节流通常用于读取或写入二进制数据(比如图片、音频等),可以使用的类有FileInputStream、ByteArrayInputStream等。
-
字符流:字符流的抽象类是Reader(字符输入流)和Writer(字符输出流),它们分别针对字符数据的读取和存储进行了抽象。字符流通常用于读取或写入文本数据,可以使用的类有FileReader、BufferedWriter等。
区别:
-
字节流操作的是8位字节,而字符流操作的是16位Unicode字符。
-
字符流比字节流多了缓冲区,因此具有更高的效率。
通常情况下,如果我们要读取或写入文本数据,建议使用字符流,因为字符流能够更好地处理编码问题。而如果需要读取或写入二进制数据,比如图片、音频等,就需要使用字节流。
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
15.Java中静态变量有什么用?
静态变量是一个类的所有实例对象所共享使用的变量,静态变量不依赖于类的任何实例,也就是说只要类被加载到内存中,静态变量就已经存在。
主要作用有:
(1)共享数据:静态变量是所有对象共享的,因此对于需要在多个对象之间共享数据的情况,就可以使用静态变量,避免在每个对象中都创建相同的数据。
(2)对象计数:在某些情况下,需要追踪类的实例对象的数量,例如计算一共创建了多少个实例对象,这时候可以使用一个静态变量来计数。
(3)程序初始化:在程序启动时,需要对一些静态数据进行初始化操作。
需要注意的是,静态变量存在的周期和类一样长,所以可能导致内存浪费。另外,静态变量修改后对于所有对象均生效,可能会影响到程序的正确性。因此,在使用静态变量的时候需要谨慎,非必要不使用。
16.重载和重写有什么区别?
在这里,我先说一下重载和重写的概念,再谈区别。
重载:指的是在一个类中定义多个同名的方法,但是这些方法的参数列表不同。Java编译器通过分析调用方法时提供的参数,来决定调用哪一个方法。重载的方法必须有不同的参数列表(个数、类型、顺序等)。
重写:指的是子类重新实现了父类中已经存在的方法,覆盖掉了父类中原有方法的实现,但是方法名、参数和返回值类型必须和父类相同。重写方法时不能降低访问权限,只能扩大访问权限。
基于上述概念,我总结出以下几点不同:
(1)定义不同:重载是指在一个类中定义多个同名的方法,但是这些方法的参数列表不同。重写是指重新实现父类中已经存在的方法,但是方法名、参数和返回值必须和父类相同。
(2)成员方法和实例方法:重载只和成员方法有关,而重写只和实例方法有关。
(3)调用区别:重载是根据调用方法时传入相应的参数来决定具体调用那个方法,而重写则是根据对象的类型(运行时类型)来确定调用那个方法。
解释上述加粗字体:
当一个父类的引用变量指向一个子类对象时,如果这个子类中重写了父类的某个方法,当引用变量调用这个方法时,实际调用的是子类中重写的方法,而不是父类中的方法,这个过程是在运行时根据对象类型确定的,因此也被称为“动态绑定”或“后期绑定”。
17.面向对象和面向过程有什么区别?
面向对象强调将现实中的事物抽象成类,类是对象的模板,对象是类的实例。面向对象的编程思想使用“封装、继承、多态”等特征来实现代码的重用和简洁性。
面向过程主要关注解决问题的过程或流程,程序代码按照步骤一步一步执行,面向过程的编程思想通常只使用“函数”来组织代码,没有类和对象的概念。
以下是代码示例:
-
// 面向对象编程思想
-
class Animal {
-
String name;
-
int age;
-
public Animal(String n, int a) {
-
this.name = n;
-
this.age = a;
-
}
-
public void say() {
-
System.out.println("我的名字是" + this.name + ",我今年" + this.age + "岁");
-
}
-
}
-
public class ObjectOrientedDemo {
-
public static void main(String[] args) {
-
Animal animal = new Animal("小狗", 2);
-
animal.say();
-
}
-
}
上面的代码中,我们定义了一个 Animal
类,它有两个属性 name
和 age
,以及一个 say()
方法,用来输出动物的信息。在 main
函数中,我们创建了一个 Animal
对象并调用 say()
方法来输出信息,这是典型的面向对象编程思想。
-
// 面向过程编程思想
-
public class ProcedureOrientedDemo {
-
public static void main(String[] args) {
-
String name = "小狗";
-
int age = 2;
-
say(name, age);
-
}
-
public static void say(String name, int age) {
-
System.out.println("我的名字是" + name + ",我今年" + age + "岁");
-
}
-
}
在面向过程代码中,我们只定义了一个主函数 main
和一个 say
函数,main
函数中直接传入 name
和 age
参数调用 say
方法来输出信息。
18.Java中的构造方法是什么?和普通方法的区别是什么?
Java中的构造方法是一种特殊的方法,用于创建和初始化对象。在Java中,每个类可以定义一个或多个构造方法,当创建对象时,会调用一个相应的构造方法来初始化对象的各个属性
构造方法和普通方法的区别在于:
- 构造方法的方法名必须与类名相同。
- 构造方法没有返回值(即没有指定返回类型),也不能使用void关键字。
- 构造方法总是在对象创建时被调用,不能手动调用。
- 构造方法可以重载,即一个类可以定义多个构造方法,只要它们的参数列表不同即可。
以下是一个实例:
-
public class Person {
-
private String name;
-
private int age;
-
// 无参构造方法
-
public Person() {}
-
// 带参构造方法
-
public Person(String name, int age) {
-
this.name = name;
-
this.age = age;
-
}
-
}
在上面的示例中,Person类定义了两个构造方法,一个是无参构造方法,一个是带参构造方法。在客户端调用构造方法时,例如:
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取
-
Person p1 = new Person(); // 使用无参构造方法创建对象
-
Person p2 = new Person("Tom", 20); // 使用带参构造方法创建对象
19.没有构造方法的类能够被初始化吗?
在Java中,如果一个类没有定义任何构造方法,编译器会自动为其生成一个无参构造方法。如果你不需要自己写构造方法,也可以不写,编译器会为你自动生成一个无参构造方法。
但是如果你定义了构造方法,编译器就不会添加默认的无参的构造方法了。
20.请你说出你对封装、继承、多态的理解
这是老生常谈的一个问题,但是究竟如何回答才能让面试官喜欢呢?
首先,封装、继承和多态是Java中面向对象编程的三大核心特征,下面我分开讲这个三个内容:
封装:封装是一种将数据和行为组合在一起的机制,数据被视为对象的成员,行为(方法)被视为对象的功能。封装可以将数据隐藏在对象内部,只能通过对象的公共方法访问。它可以提高代码的重用性和可维护性,并且在保证对象数据的安全性的同时,可以改善代码的可读性。
继承:继承是通过重用代码来创建新类的机制,它能够使已存在的类成为新类的基础,避免了重复编写类的过程。在Java中,可以通过extends关键字实现继承关系。子类可以继承父类的属性和方法,并且还可以扩展更多的属性和方法。继承可以简化代码的编写,提高代码的重用性,同时也可以实现代码的多态性。
多态:多态是一种添加灵活性和可扩展性的技术,它允许同一操作作用于多个不同的对象,以不同的方式表现出行为,实现了程序的高扩展性和可维护性。在Java中,多态有三种形式:重载、重写和接口实现。重载是指同一类中存在多个同名方法,但是参数列表不同;重写是指子类中重写了父类的同名方法,参数列表和返回值必须相同;接口实现是指实现了多个接口,可通过这些接口调用不同的方法。
接下来,我用代码示例:
封装:
-
public class Person {
-
private String name;
-
private int age;
-
// 构造方法
-
public Person(String name, int age) {
-
this.name = name;
-
this.age = age;
-
}
-
// 公共方法
-
public String getName() {
-
return this.name;
-
}
-
public void setName(String name) {
-
this.name = name;
-
}
-
public int getAge() {
-
return this.age;
-
}
-
public void setAge(int age) {
-
this.age = age;
-
}
-
}
在上面的例子中,使用private访问修饰符来限制变量name和age的访问,只能通过提供的公共方法getName()和getAge()来访问和修改变量的值,这就实现了封装。
继承:
-
public class Animal {
-
public void run() {
-
System.out.println("Animal is running");
-
}
-
}
-
public class Dog extends Animal {
-
public void run() {
-
System.out.println("Dog is running");
-
}
-
public void bark() {
-
System.out.println("Dog is barking");
-
}
-
}
在上面的例子中,类Dog继承了类Animal,并覆盖了方法run(),使得狗的行为与动物不同。另外,还新增了狗特有的方法bark()。
多态:
-
public class Animal {
-
public void run() {
-
System.out.println("Animal is running");
-
}
-
public void eat() {
-
System.out.println("Animal is eating");
-
}
-
public void sleep() {
-
System.out.println("Animal is sleeping");
-
}
-
}
-
public class Dog extends Animal {
-
public void run() {
-
System.out.println("Dog is running");
-
}
-
public void bark() {
-
System.out.println("Dog is barking");
-
}
-
}
-
public class Polymorphism {
-
public static void main(String[] args) {
-
Animal animal1 = new Animal();
-
Animal animal2 = new Dog();
-
Dog dog = new Dog();
-
animal1.run();
-
animal1.eat();
-
animal1.sleep();
-
animal2.run();
-
animal2.eat();
-
animal2.sleep();
-
dog.run();
-
dog.eat();
-
dog.sleep();
-
dog.bark();
-
}
-
}
在上面的例子中,使用重载和重写方法实现多态。
标签:面试题,八股文,构造方法,对象,线程,Java,方法,public From: https://blog.csdn.net/2401_87705025/article/details/142757516篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处即可】免费获取