java 面试题总结(基础篇)
一、编程基础
1.JDK、JRE、JVM的区别
JVM(Java Virtual Machine):
Java虚拟机,负责加载、执行字节码文件(.class)和垃圾回收等任务。
JRE(java Runtime Environment):
Java运行环境,包含了JVM和java程序运行时库API
JDK(Java Development Kit):
Java开发工具包,包含了JRE和java开发工具
2.基本数据类型有哪些?
byte:字节型,存整数,占1个字节,范围-128到127
short:短整型,占2个字节,-32768到32767
int:最常用 整形,占4个字节,-2^ 31到2^31
long:长整型,占8个字节
float:单精度浮点数,存小数,占4个字节,赋值要加f或F,不能表示精确的数
double:双精度浮点数,占8个字节,不能表示精确的数
boolean:布尔型,只能存储true或false,占1个字节
char:字符型,采用Unicode编码格式,存单个字符,占2个字节
3.说说java中的数组
在Java中数组是用于存储相同类型元素的容器。是等长对象,可以通过索引来访问和操作元素。使用前需要被声明。
type[] arrayName; //声明数组
arrayName=new type[length];
//赋值
int[] numbers=new int[5];
for(int i=0;i<numbers.length;i++){
System.out.println(numbers[i]));
}
//或者直接赋值
int[] numbers={1,2,3,4,5};
多维数组,是多个一维数组组成的数据结构,可以表示一个表格或矩阵,具有行、列的概念,第一个索引代表行,第二个代表列
4.Java中的三大特性是什么?各个描述一下优劣势
封装:允许将数据和方法包装在一个类中,并对外隐藏内部细节。
优势:
①数据隐藏:定义私有(private)关键字,避免外界直接访问和修改对象的数据,提高了数据的安全性和可靠性。
②隔离复杂性:将类的视线细节隐藏起来,质保路必要的接口给外界使用者,减少了对外部的依赖,减低了类之间的复杂性。
③提供公共接口:定义public公共接口,对外提供统一的访问和操作方式,提供更好的交互方法,便于使用和维护。
劣势:需要对内部成员的可见性仔细控制,增加了程序员的代码细节要求
继承:允许一个派生类通过extends关键字继承基类,可以使用基类的属性和方法
优势:
**①代码复用:**派生类可以直接使用负类属性和方法,提高了代码复用性
**②扩展性:**派生类可以重写基类方法,在继承的方法上进修修改扩展,添加新的属性和方法,以满足不同需求
劣势:
①层次过深复杂:增加了类之间的关系和依赖,强耦合,导致基类修改时,可能会影响所有的派生类
②单一继承限制: 一个类只能继承一个基类,某些情况下会限制程序设计。
克服继承的劣势,可以采用**接口(Interface)**等替代继承,可实现多个接口
**多态:**有多种形态,同一个类型的变量引用不同类型的对象,动态的执行相应的方法。
条件:
存在继承关系、重写基类的方法、用基类类型的引用变量来引用派生类对象
优势:
**①灵活性:**通过统一的接口来操作不同的对象,提高了代码的灵活性和可维护性。当需求变化时,只需要更换具体的子类对象,而无需修改现有的代码。
**②方法的回调:**可以将一个对象作为参数传递给其他方法
③提高代码可读性 :代码更易读
劣势:
过度抽象化、开销不确定、复杂性提高
5.重写和重载的区别
重写:发生在父子类中,方法名相同,参数列表相同,方法体不同
重载:同一类中,方法名相同,参数列表不同
6.描述一下抽象类的特点
abstract关键字修饰,可用在变量、常量、方法,派生类可继承,有抽象方法的必须得是抽象类,派生类可重写抽象方法
7.== 和equals方法的含义
== :比较的是变量(栈)内存中存放的对象(堆)的内存地址,用于判断两个对象地址是否相同
①两边的操作数必须是同一类型(包括父子类)
②当两边对象里是具体的数字,则是比较值相同
equals:比较两个对象的内容是否相等(重写后),重写前在比较引用类型的变量时和==一样。
8.说说JAVA中的this关键字
表示当前对象
①引用当前对象
②解决命名冲突,方法内部存在与成员变量同名时,可以用this防冲突,表示当前对象成员变量
③引用其他构造方法,可用用this(其他构造方法)调用
9.static关键字的作用
①修饰变量:
表示静态变量,属于类。在程序启动时就会被初始化,且该类的所有实例共享同一个静态变量,类名直接访问
②修饰方法:
表示静态方法,类名直接调用,无需创建实例,只能直接访问静态变量和调用静态方法,不能被重写只能被隐藏
③静态代码块:
在类加载时就执行且一次,用于类的初始化操作
二、API应用
1.String/StringBuffer/StringBuilder有什么不同
①可变性:
String 是不可变的类,一旦创建了对象就不能修改其内容,对字符串进修拼接、插入或删除等都会重新创建新的对象
StringBuffer和StringBuilder是可变的类,支持对字符串动态修改,是用地址指向对象
②线程安全性:
String是安全的,适用于多线程
StringBuffer是线程安全的,方法使用了synchronized关键字进修同步
StringBuilder是线程不安全的
③性能
StringBuilder > StringBuffer 线性安全影响了性能。
String 在常量池的使用上是性能优化了的,但是在频繁的字符串拼接操作中,会创建大量的临时对象,性能会受到影响
2.说说ArrayList的特点及应用场景
ArrayList是java中的集合的一种常用的动态数组实现类,继承了AbstractList抽象类
特点:
①动态数组:底层是具有动态扩容的特性,添加和删除元素时,可以自动调整底层数组大小
②随机访问:获取元素是通过索引随机访问
③允许存储重复元素:通过索引访问和修改
④非线性安全:不适用多线程的并发操作
⑤插入和删除效率较低:需要移动其他元素
应用场景:需要快速访问元素频繁访问的场景
3.LinkedList的特点及使用场景
java中常用的双向链表实现类
- 高效的插入和删除:
只需要修改节点的引用,不需要进行元素的移动和复制 - 可用作栈和队列:实现了Deque接口
- 非线性安全
- 应用场景:频繁进行插入和删除操作的场景
4.HashMap特点及应用场景
- 高效的查找和插入操作:HashMap的底层是通过哈希表实现的,使用率哈希函数将键转换为数组索引。
- 键值对存储:每个键都是唯一的。通过键可以快速查找对应的值。HashMap中的键和值都可以为null,但一个HashMap只能有一个null键。
- 无序的元素:元素的存储顺序不固定,如果需要有序,可以考虑使用LinkedHashMap
- 线程不安全:多线程环境下可能会导致数据不一致的问题。如果需要在多线程下安全的使用,可以考虑ConcurrentHashMap
- 可变大小:动态调整,根据实际元素数量自动扩容和收缩
- 应用场景:适用于根据键快速查找对应值的时候,缓存、数据索引、字典等
4. 泛型及应用
提供了编译时类型安全监测机制,本质是参数化类型,允许类、接口、方法的定义中使用类型参数,指定数据类型,提高了代码的安全性和可读性
-
泛型类和接口 :
通过尖括号<>指定类型 List<E> 表示一个可以存放元素类型为E的列表 -
类型通配符:
当我们不知道用什么类型时,用?和?extends Type
? 代表位置类型
? extends Number 可以存放任意Number类型或其子类的列表
? super Integer 表示存放Integer类型或其类型的父类类型 -
泛型擦除:
只是在编译器这个层次实现,字节码文件中是不包含泛型的类型信息的,编译时被去掉,这就是泛型擦除 -
避免了强制类型转换
-
重用性提高:
将类型参数化,可以使得同一段代码可以使用与不同类型的数据
5.创建对象的方式
- 使用new关键字
- 使用Class类的newInstance() 方法,反射机制
MyObject obj=MyClass.class.newInstabnce()
- 使用Constructor类的newInstance()方法,反射机制
Construct<MyClass> constructor=MyClass.class.getConstructor(String.class)
MyClass obj=constructor.new Instance("parameter")
- 使用clone()方法,类实现Cloneable接口
MyObject obj2=(MyObject) obj.clone();
- 使用反序列化:类实现Serializable接口,将对象转换为字节序列,再将序列转换为对象
ObjectInputStream in=new ObjectInputStream(new FileInputStream("file.ser"));
MyObject obj=(MyObject)in.readObject();
6.细说一下Java线程对象状态
- 新建状态(New):
线程对象创建后尚未启动,即没有调用start()方法,初始化操作可以进行 - 就绪状态(Runnable):
线程对象已经准备好执行,并且等待获取CPU时间片 - 运行状态(Running):
正在执行run()方法中的代码 - 阻塞状态(Blocked):
线程对象由于某些原因暂时无法执行,需要等待唤醒或满足特点条件 - 等待状态(Waiting):
等待其他线程的特点操作,无法被唤醒,wait(),join(),park()方法等 - 终止状态(Terminated):
执行完run()方法,或者出现了异常而终止
7.说一下synchronized关键字
用于实现线程之间的同步,保证多线程访问共享资源时的安全性
- 修饰方法:
使用该关键字修饰的方法称为同步方法。当线程访问该方法时,会自动获得该方法所在对象的监视器,其他线程则被阻塞,直到该线程执行完,释放锁
是隐式获取锁的方式,锁是该方法所在对象的实例锁 - 修饰代码块:
称为同步块,需要指定获取锁的对象或者类
锁可以是任意对象,也可以是使用this关键字表示当前对象的实例锁,还可以使用类名.class表示类锁
8.说说对反射技术的理解
反射技术是Java中一种强大的功能,允许程序 运行时动态地获取和操作类的名称、方法、属性等信息,动态的创建对象,调用方法,访问和修改属性及执行其他与类相关的操作。
核心是一组类和接口:
Class类(类或接口)、Constructor类(构造方法)、Method类(方法)、Field类(属性)
- 先获取类的字节码对象
①通过class.类名
②forName(包名.类名)方法 - 动态创建对象
Class类的newInstance方法,相当于无参构造
Class<?> clazz = MyClass.class;
Object obj = clazz.newInstance();
- 动态调用方法
Method类的invoke()方法可以调用类的方法
Method method = clazz.getMethod("methodName", int.class);
Object result = method.invoke(obj, 123);
- 动态访问和修改字段
通过Filed类
Class<?> clazz = MyClass.class;
Object obj = clazz.newInstance();
Field field = clazz.getDeclaredField("fieldName");//此field是你的属性名
field.setAccessible(true); // 设置访问权限
Object value = field.get(obj); // 获取字段的值
field.set(obj, newValue); // 设置字段的值
使得程序具有更大的灵活性和扩展性,同时也带来了一定的性能开销