1、== 和 equals 的区别是什么?
- equals 只能用在引用数据类型中,== 可以用来比较基本类型和引用数据类型
- 基本类型用 == 比较其值的大小
- 引用类型用 == 比较其地址的大小,引用类型一般会重写equals 方法用来比较值的大小(未重写的equals 方法也是比较地址的大小)
2、java final 关键字的作用 关于 final 的总结
final 表示最终的,结束的。一般可以用来修饰变量、类、方法。
好处:主要用作安全考虑。
- 修饰的成员变量只能被赋值一次。
final 修饰 局部变量必须在使用前赋值
final 修饰 成员变量如果定义时没有赋值称为(空白final),空白final 必须在构造器或者静态代码块中赋值。 - 修饰的类无法被继承,并且类中的方法默认时 final 的
- 修饰的方法无法被重写,private类型的方法默认是final的
3、java static 关键字的作用 关于 static 的总结
static 表示静态的,主要用于内存管理。
相当于类纬度的,而不是对象纬度的。通常会用来修饰变量,修饰类,修饰方法,修饰代码块
好处:节省内存,使用方便,增加交互性,常见的时用于工具类。
-
修饰成员变量
外部可以直接通过类名.访问静态成员变量
当且仅当在类初次加载时会被初始化
所有类对象共享,内存中值存储一份 -
修饰类,只有内部类才能用 static 修饰
外部可以直接通过类名.类名访问静态内部类
只能访问外部类的静态成员和方法,不可以访问普通非静态的。 -
修饰方法
外部可以直接通过类名.访问静态方法
并且静态方法中只能访问静态变量和静态方法,不可以访问普通非静态的。
静态方法中不可以使用 this 、super 等关于对象的关键字 -
修饰代码块
通常用来初始化静态变量
只能置于类中,不能置于方法体中(普通代码块可以)
4、String test = new String(“a”) + new String(“b”) 会创建几个对象?
【前提,常量数据池中没有 “a”, “b”】
对象一:常量数据池中创建 “a”
对象二:常量数据池中创建 “b”
对象三:堆上开辟空间,copy 对象一,创建第二个 “a”,
对象四:堆上开辟空间,copy 对象二,创建第二个 “b”
对象五:new StringBuilder()
对象六:堆上开辟空间,创建拼接好的"ab"
5、抽象类和接口的区别
抽象类:用 abstract 修饰的类,该类无法被实例化,只有被继承才有意义。抽象类不可以用 final 修饰(final 修饰的类无法被继承),抽象类可以没有抽象方法,但是包含抽象方法的一定时抽象类。抽象类在被继承时,子类是非抽象类时必须重写父类的抽象方法,子类依旧时抽象类时可以不重写。
接口:接口可以理解为一种特殊的类。也可以有成员变量和成员方法。成员变量默认是被 static final 修饰的,只能在声明的时候赋值。接口中的方法默认是抽象方法,如果要实现普通方法必须用 default 标识。接口之间可以是多继承,接口的实现类必须重写接口中的抽象方法。
相同点
- 无法被实例化
- 抽象类的子类和接口的实现类只有重写了抽象方法才可以被实例化
不同点
- 抽象类被继承用的是 extends 关键字,接口被实现用的是 implements 关键字,一个类可以实现多个接口,但只能继承一个类
- 抽象类中的抽象方法可以是 public 和 protected 的,接口中的抽象方法只能是 public 的
- 抽象类与其子类是 is-a 的关系(子类是父类),接口与其实现类是 has-a 的关系(实现类中有接口的某某功能)
/*
蝙蝠是一种动物,所以继承了 Animal 类,重写了 showType 方法
蝙蝠属于哺乳动物,所有具有哺乳动物的特点
蝙蝠会飞,所以具有飞行动物的特点
*/
public class Bat extends Animal implements Fly , Mammal{
@Override
public void showType() {
type = "Bat";
System.out.println("the type is " + type);
}
@Override
public void fly() {
System.out.println(type + " can fly !");
}
@Override
public void mammal() {
System.out.println(type + " is mammal !");
}
}
6、Java是如何实现跨平台功能的?
- java 是一种高级编程语言,能构在不同平台不同机器上运行,这归功于代码执行底层其实是jvm 虚拟机完成的(Java平台的核心组件之一,它是一个基于栈的执行引擎)。
- 程序员编写的 java文件经过编译器编译后生成机器可以识别的字节码文件.class文件,然后才能被虚拟机所解释执行(Java字节码是一种中间语言,类似于汇编语言,机器可以识别,人不可识别),由于虚拟机在不同的机器上的实现是相同的。
- Java虚拟机有明确的规范 和 Java虚拟机详细实现过程(。。。。。 )
Java字节码文件具有以下优点:
- 可移植性:Java字节码文件在任何Java虚拟机上都可以运行,因此它们具有可移植性。
- 安全性:由于Java字节码文件只包含中间代码,不包含原始指令或可执行代码,因此它们是安全的。Java虚拟机提供了严格的安全性控制,以确保Java应用程序的安全性。
- 高效性:Java字节码文件的解释器被编写为高度优化的本地代码。这使得Java应用程序在各种平台上都可以高效地运行。
7、类加载过程
整个加载过程其实是将编译后的.class 文件加载在 jvm 进行解析使用回收的过程。具体分为 加载,链接(验证、准备、解析),初始化,使用,卸载。
- 加载 根据类名读取类的整个二进制流到 jvm 中,并存储在运行时内存区中的方法区;将其转换为一个 java.lang.Class 对象实例,作为方法区中的链接入口。
- 链接 整个就是验证加载的正确性
验证:进行 final 是否合规判断,静态变量是否合法判断
准备:静态变量赋初始值
解析:解析类的方法,确保与类之间的相互引用正确性,完成内存结构布局 - 初始化 将类中 static 关键字标识的代码统一执行一遍。每个类都有< clinit > 初始化方法,里面存储用户指定的值,覆盖初始值。并且优先初始化父类。
- 使用 就是执行方法区的代码
- 卸载就是垃圾回收
参考大佬文章
8、java 反射的了解
java 代码在编译后会将关于类的所有信息存储下来,当程序在运行时可以通过反射的方式获取类的所有信息,(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分,其中也包含 private 私有属性。
主要体现在:
- 可以通过 class 对象反向解析出类的信息,运行时动态创建对象和编译,体现出灵活性
- 常见于各种框架中,注解的使用;还有 AOP 切面编程应用到日志输出上
public class ReflectionTest {
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
Person person = new Person();
person.age = 26;
person.name = "周芷若";
test1(person);
}
public static void test1(Object object) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class c = object.getClass();
Field ageField = c.getDeclaredField("age");
System.out.println(ageField.getInt(object));
Method showMethod = c.getMethod("show");
showMethod.setAccessible(true); // 设置为可达。暴力反射,使得私有方法可以运行
showMethod.invoke(object);
}
}
class Person{
int age;
String name;
private void show(){
System.out.println("name:"+ name);
}
}
9、什么是 java 序列化?什么情况下需要序列化
序列化是将对象转化为字节流,反序列化是将字节流转化为对象。
对象是无法在网络中进行传输的,也无法直接保存到文件中(这两个过程都是通过输入输出流处理的)
java 序列化是通过serializable 接口,这个接口没有需要实现的方法,仅用于标识可序列化的语义。序列化运行时与每个可序列化类关联一个版本编号,称为serialVersionUID,在反序列化期间使用验证的发送方和接收方。
序列化时会用一个输出流FileOutputStream 构造 ObjectOutputStream 对象,然后调用 writeObejct 方法负责写入对象流状态,反序列化时则使用输入流对象。
10、什么是深拷贝,什么是浅拷贝
当我们需要完全复制一个对象时,可以直接使用已有的拷贝克隆方法
其中有深拷贝和浅拷贝两种
浅拷贝:只对基本类型进行拷贝,引用类型只进行引用指向,内部的值还是复用的。此时如果修改引用某一处的引用对象值,其他对象中的引用对象值也随着改变。
深拷贝:也会将引用对象的值进行拷贝,如果有多级引用对象均会进行拷贝。
常见的身拷贝方法:
- 实现 Cloneable 接口,并且重写 clone() 方法,在clone() 方法中调用 super.clone()方法进行浅拷贝,对需要深拷贝的地方在递归进行拷贝。
- 使用序列化和反序列化,将对象进行序列化为字节流,再将字节流反序列化为新的对象
- 第三方工具:Spring Framework 的 ObjectUtils.clone() 。