先罗列本篇文章包含的Java 常见面试的主题:
- 多线程与多进程面试
- 常见设计模式
- JVM 底层
关注我们,更多技术干货:
Java微服务实战296集大型视频-谷粒商城【附代码和课件】
Java开发微服务畅购商城实战【全357集大项目】-附代码和课件
2021年JAVA 精心整理的常见面试题-附详细答案
https://mikejun.blog.csdn.net/article/details/114488339
2021年- 精心整理的 SpringMVC 常见面试题-【附详细答案】
https://mikejun.blog.csdn.net/article/details/114992529
2021年JAVA 面试题之–数据结构篇【附详细答案】
https://mikejun.blog.csdn.net/article/details/114647742
精心整理的计算机各类别的电子书籍【超全】
https://mikejun.blog.csdn.net/article/details/115442555
三天刷完《剑指OFFER编程题》–Java版本实现(第一天)
https://mikejun.blog.csdn.net/article/details/106996017
三天刷完《剑指OFFER编程题》–Java版本实现(第二天)
https://mikejun.blog.csdn.net/article/details/108098502
三天刷完《剑指OFFER编程题》–Java版本实现(第三天)
https://mikejun.blog.csdn.net/article/details/108253489
一、Java基础面试题
1.Java中 == 和 equals 的区别
首先看一下 Java 的基本类型
1.1基本类型数据:
byte,short,char,int,long,float,double,boolean 他们之间的比较应该使用(==),比较的是他们的值。
equals 比较的是引用是否相同
? ? ? ? String s1 = "abc";
? ? ? ? String s2 = "abc";
? ? ? ? String s3 = new String("abc");
?
? ? ? ? System.out.println(s1 == s2); // true
? ? ? ? System.out.println(s1 == s3); // false
? ? ? ? System.out.println(s3.equals(s1)); // true
1.2引用类型数据:
a, 当使用 == 比较的时候,比较的是 他们在内存中的存放地址。
String a = "abc";
String b = "abc";
System.out.println(a == b);//true
b, 当使用 equals 比较时,这个方法的初始行为是比较对象在堆内存中的地址。
equals()方法是用来判断其他的对象是否和该对象相等.
//equals()方法在object类中定义如下:?
public boolean equals(Object obj) { ?
? ? return (this == obj); ?
} ?
但在一些诸如String,Integer,Date类中把Object中的这个方法覆盖了,作用被覆盖为比较内容是否相同。
Math、Integer、Double等这些类都是重写了equals()方法的,从而进行的是内容的比较,而不再是地址的比较。当然,基本类型是进行值的比较。
String 的内存分配图
String str1= "hello"; ??
String str2= new String("hello"); ??
String str3= str2; ??
从图中可以发现每个String对象的内容实际是保存到堆内存中的,而且堆中的内容是相等的,但是对于str1和str2来说所指向的地址堆内存地址是不等的,所以尽管内容是相等的,但是地址值是不相等的
“”是用来进行数值比较的,所以str1和str2比较不相等,因为str2和str3指向同一个内存地址所以str2和str3是相等的。所以“”是用来进行地址值比较的。
2. 如何Java 重写equals() 方法
重写 equals()方法的模板:
package MyDemo;
import java.util.Objects;
public class Human {
private int age;
private String name;
public Human(int age, String neme){
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int hashCode(){
return Objects.hash(age, name);
}
public boolean equals(Object obj){
// 测试两个对象是否相同
if (this == obj) return true;
// 检测对象是否为空
if (obj == null) return false;
// 检测两个对象所属于的类是否相同;
if (this.getClass() != obj.getClass()) return false;
// 对 otherObject 进行类型转换和 类 Human 对象比较
Human other = (Human)obj;
return Objects.equals(name, other.name) && age == other.age;
}
public static void main(String[] args) {
Human human = new Human(14, "zhangsan");
Human human1 = new Human(14, "zhangsan");
System.out.println(human.equals(human1)); // 初始没有重写equals 为false
System.out.println(human.hashCode());
System.out.println(human1.hashCode()); // 如果 name 和 age 都是相同的话,哈希值相同
}
}
为什么重写equals方法之前要重写hashCode方法:
因为 Object规范中说到: 相等的对象必须具有相等的散列码
因为hashCode散列码的目的是为了HashSet、HashMap、HashTable比较的时候缩小范围空间,它只是返回一个散列整数然后根据散列码去散列桶中查找对象区间。它不保证对象是否是相等的
3. 抽象类和接口的区别
1. 抽象类:
抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类作为很多子类的父类,它是一种模板式设计。
抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,男人和女人都是属于人类,它们都有一些共性,比如有眼睛、鼻子、会走路等等,就可以把这些方法抽象出来。
继承是一个 "是不是"的关系,如 男人 是不是 人类
public abstract class Employee
{
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) // 构造方法
{
System.out.println("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public double computePay() // 自己实现的方法
{
System.out.println("Inside Employee computePay");
return 0.0;
}
public abstract double computePay(); // 抽象方法
//其余代码
}
1. 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
2. 抽象类相当于时定义一个类模板,是用来继承的,所以不能直接实例化
3.抽象类中的抽象方法只是声明,不给出方法的具体实现
4. 如果类中有抽象方法,则必须是抽象类
5.抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
2. 接口:
接口是一种行为规范,没有继承的关系,接口 实现则是 "有没有"的关系,比如 定义一个接口 Fly(),
但 男人 这个类实现 飞 这个接口时,相当于拥有了这项 飞 的功能。所以,接口有扩展功能的作用。
interface Animal {
public static final String NAME = "god";
public void eat();
public void travel();
}
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法,可以实现多继承。
3. 抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
4 说一说面向对象的特征
包括有:封装,继承,多态和抽象
封装
封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变它内部的数据。在 Java 当中,有 3 种修饰符: public, private 和 protected。每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。
下面列出了使用封装的一些好处:
通过隐藏对象的属性来保护对象内部的状态。
提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。
禁止对象之间的不良交互,提高模块化继承
继承
给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。
多态
多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作可以应用到其他类型的值上面。
抽象
抽象是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细节来创建类。
Java 支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。
5 重载和重写的区别
override(重写)
方法名、参数、返回值相同。
子类方法不能缩小父类方法的访问权限。
子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
存在于父类和子类之间。
方法被定义为final不能被重写。
overload(重载)
参数类型、个数、顺序至少有一个不相同。
不能重载只有返回值不同的方法名。
存在于父类和子类、同类中。
6 说说反射的用途及实现
Java反射机制主要提供了以下功能:在运行时构造一个类的对象;判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架
Java反射的主要功能:
确定一个对象的类
取出类的modifiers,数据成员,方法,构造器,和超类.
找出某个接口里定义的常量和方法说明.
创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).
取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.
在运行时刻调用动态对象的方法.
创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.
反射的应用很多,很多框架都有用到
spring 的 ioc/di 是反射….
javaBean和jsp之间调用也是反射….
struts的 FormBean 和页面之间…也是通过反射调用….
JDBC 的 classForName()也是反射……
hibernate的 find(Class clazz) 也是反射….
7 静态变量和实例变量的区别?
**在语法定义上的区别:**静态变量前要加static关键字,而实例变量前则不加。
**在程序运行时的区别:**实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
8 访问修饰符public,private,protected,以及不写(默认)时的区别?
修饰符
当前类
同包
子类
其他包
public
√
√
√
√
protected
√
√
√
×
default
√
√
×
×
private
√
×
×
×
类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。
9 int、char、long各占多少字节数
Java 基本类型占用的字节数
1字节: byte , boolean
2字节: short , char
4字节: int , float
8字节: long , double
注:1字节(byte)=8位(bits)
10 详细解释一下内部类
内部类使得多重继承的解决方案变得更加完整
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响
使用内部类才能实现多重继承
内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
创建内部类对象的时刻并不依赖于外围类对象的创建。
内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
内部类提供了更好的封装,除了该外围类,其他类都不能访问。
当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员
成员内部类
在成员内部类中要注意两点
成员内部类中不能存在任何 static 的变量和方法
成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类
静态内部类
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:
它的创建是不需要依赖于外围类的。
它不能使用任何外围类的非static成员变量和方法。
11 说明一下public static void main(String args[])这段声明里每个关键字的作用
public: main方法是Java程序运行时调用的第一个方法,因此它必须对Java环境可见。所以可见性设置为pulic.
static: Java平台调用这个方法时不会创建这个类的一个实例,因此这个方法必须声明为static。
void: main方法没有返回值。
String是命令行传进参数的类型,args是指命令行传进的字符串数组。
12 为什么Java里没有全局变量
答案:全局变量是全局可见的,Java不支持全局可见的变量,因为:全局变量破坏了引用透明性原则。全局变量导致了命名空间的冲突。
13. 如果不借助中间变量交换两个变量的值?
先把两个值相加赋值给第一个变量,然后用得到的结果减去第二个变量,赋值给第二个变量。再用第一个变量减去第二个变量,同时赋值给第一个变量。代码如下:
int a=5,b=10;a=a+b; b=a-b; a=a-b;
使用异或操作也可以交换。第一个方法还可能会引起溢出。异或的方法如下:
int a=5,b=10;a=a+b; b=a-b; a=a-b;
int a = 5; int b = 10;
a = a ^ b;
b = a ^ b;
a = a ^ b;
14 Java有没有goto?
答:goto 是Java中的保留字,在