2023-JavaSE最新整理面试题
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
Java基础面试题
讲师邓澎波
一、面向对象和集合专题
1. 面向对象和面向过程的区别
面向过程是分析解决问题的步骤然后用函数把这些步骤一步一步地实现然后在使用的时候一一调用则可。性能较高所以单片机、嵌入式开发等一般采用面向过程开发
面向对象是把构成问题的事务分解成各个对象而建立对象的目的也不是为了完成一个个步骤而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说比面向过程要低。
2. 介绍下Java中的基本数据类型
基本类型 | 大小(字节) | 默认值 | 封装类 |
---|---|---|---|
byte | 1 | (byte)0 | Byte |
short | 2 | (short)0 | Short |
int | 4 | 0 | Integer |
long | 8 | 0l | Long |
float | 4 | 0.0f | Float |
double | 8 | 0.0d | Double |
boolean | - | false | Boolean |
char | 2 | \u0000(null) | Character |
需要注意
- int是基本数据类型Integer是int的封装类是引用类型。int默认值是0而Integer默认值是null所以Integer能区分出0和null的情况。一旦java看到null就知道这个引用还没有指向某个对象再任何引用使用前必须为其指定一个对象否则会报错。
- 基本数据类型在声明时系统会自动给它分配空间而引用类型声明时只是分配了引用空间必须通过实例化开辟数据空间之后才可以赋值。数组对象也是一个引用对象将一个数组赋值给另一个数组时只是复制了一个引用所以通过某一个数组所做的修改在另一个数组中也看的见。虽然定义了boolean这种数据类型但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令Java语言表达式所操作的boolean值在编译之后都使用Java虚拟机中的int数据类型来代替而boolean数组将会被编码成Java虚拟机的byte数组每个元素boolean元素占8位。这样我们可以得出boolean类型占了单独使用是4个字节在数组中又是1个字节。使用int的原因是对于当下32位的处理器CPU来说一次处理数是32位这里不是指的是32/64位系统而是指CPU硬件层面具有高效存取的特点。
3. 标识符的命名规则
标识符的含义 是指在程序中我们自己定义的内容譬如类的名字方法名称以及变量名称等等都是标识符。
命名规则硬性要求 标识符可以包含英文字母0-9的数字$以及_ 标识符不能以数字开头标识符不是关键字
命名规范非硬性要求 类名规范首字符大写后面每个单词首字母大写大驼峰式。 变量名规范首字母小写后面每个单词首字母大写小驼峰式。 方法名规范同变量名。
4. instanceof关键字的作用
instanceof 严格来说是Java中的一个双目运算符用来测试一个对象是否为一个类的实例用法
为
boolean result = obj instanceof Class
其中 obj 为一个对象Class 表示一个类或者一个接口当 obj 为 Class 的对象或者是其直接或间接子类或者是其接口的实现类结果result 都返回 true否则返回false。
注意编译器会检查 obj 是否能转换成右边的class类型如果不能转换则直接报错如果不能确定类型则通过编译具体看运行时定。
int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型不能是基本类型
System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是如果 obj 为 null那么将返回 false。
System.out.println(null instanceof Object);
5.重载和重写的区别
重写(Override)
从字面上看重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法但有时子类并不想原封不动的继承父类中的某个方法所以在方法名参数列表返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下 对方法体进行修改或重写这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Son s = new Son();
s.sayHello();
}
public void sayHello() {
System.out.println("Hello");
}
}
class Son extends Father{
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("hello by ");
}
}
重写 总结
- 发生在父类与子类之间
- 方法名参数列表返回类型除过子类中方法的返回类型是父类中返回类型的子类必须相同
- 访问修饰符的限制一定要大于被重写方法的访问修饰符public>protected>default>private)
- 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
重载Overload
在一个类中同名的方法如果有不同的参数列表参数类型不同、参数个数不同甚至是参数顺序不同则视为重载。同时重载对返回类型没有要求可以相同也可以不同但不能通过返回类型是否相同来判断重载。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father s = new Father();
s.sayHello();
s.sayHello("wintershii");
}
public void sayHello() {
System.out.println("Hello");
}
public void sayHello(String name) {
System.out.println("Hello" + " " + name);
}
}
重载 总结
- 重载Overload是一个类中多态性的一种表现
- 重载要求同名方法的参数列表不同(参数类型参数个数甚至是参数顺序)
- 重载的时候返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准
6.HashCode的作用
java的集合有两类一类是List还有一类是Set。前者有序可重复后者无序不重复。当我们在set中插入的时候怎么判断是否已经存在该元素呢可以通过equals方法。但是如果元素太多用这样的方法就会比较满。于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域每个对象可以计算出一个哈希码可以将哈希码分组每组分别对应某个存储区域根据一个对象的哈希码就可以确定该对象应该存储的那个区域。hashCode方法可以这样理解它返回的就是根据对象的内存地址换算出的一个值。这样一来当集合要添加新的元素时先调用这个元素的hashCode方法就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素它就可以直接存储在这个位置上不用再进行任何比较了如果这个位置上已经有元素了就调用它的equals方法与新元素进行比较相同的话就不存了不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了几乎只需要一两次。
7.介绍下Java中的四种引用
强引用
强引用是平常中使用最多的引用强引用在程序内存不足OOM的时候也不会被回收使用
方式
String str = new String("str");
System.out.println(str);
软引用
软引用在程序内存不足时会被回收使用方式
// 注意wrf这个引用也是强引用它是指向SoftReference这个对象的
// 这里的软引用指的是指向new String("str")的引用也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
可用场景 创建缓存的时候创建的对象放进缓存中当内存不足时JVM就会回收早先创建的对象。
弱引用
弱引用就是只要JVM垃圾回收器发现了它就会将之回收使用方式
WeakReference<String> wrf = new WeakReference<String>(str);
可用场景 Java源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用我的理解就是一旦我不需要某个引用JVM会自动帮我处理它这样我就不需要做其它操作。
虚引用
虚引用的回收机制跟弱引用差不多但是它被回收之前会被放入 ReferenceQueue 中。注意哦其它引用是被JVM回收后才被传入 ReferenceQueue 中的。由于这个机制所以虚引用大多被用于引用销毁前的处理工作。还有就是虚引用创建的时候必须带有 ReferenceQueue 使用例子
PhantomReference<String> prf = new PhantomReference<String>(new String("str"),
new ReferenceQueue<>());
可用场景 对象销毁前的一些操作比如说资源释放等。 Object.finalize() 虽然也可以做这类动作但是这个方式即不安全又低效
上诉所说的几类引用都是指对象本身的引用而不是指Reference的四个子类的引用(SoftReference等)。
8.有没有可能两个不相等的对象有相同的hashcode
能.在产生hash冲突时,两个不相等的对象就会有相同的 hashcode 值.当hash冲突产生时,一般
有以下几种方式来处理:
- 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链
表被分配到同一个索引上的多个节点可以用这个单向链表进行存储. - 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总
能找到,并将记录存入 - 再哈希:又叫双哈希法,有多个不同的Hash函数.当发生冲突时,使用第二个,第三个….等哈希函数
计算地址,直到无冲突
9.深拷贝和浅拷贝的区别是什么?
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指
向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.
深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向
被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的
对象都复制了一遍.
10.static都有哪些用法?
所有的人都知道static关键字这两个基本的用法:静态变量和静态方法.也就是被static所修饰的变量/
方法都属于类的静态资源,类实例所共享.
除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:
public calss PreCache{
static{
//执行相关操作
}
}
此外static也多用于修饰内部类,此时称之为静态内部类.
最后一种用法就是静态导包,即 import static .import static是在JDK 1.5之后引入的新特性,可以用
来指定导入某个类中的静态资源,并且不需要使用类名,可以直接使用资源名,比如:
import static java.lang.Math.*;
public class Test{
public static void main(String[] args){
//System.out.println(Math.sin(20));传统做法
System.out.println(sin(20));
}
}
11. 介绍下Object中的常用方法
clone 方法
保护方法实现对象的浅复制只有实现了 Cloneable 接口才可以调用该方法否则抛出
CloneNotSupportedException 异常深拷贝也需要实现 Cloneable同时其成员变量为引用类型
的也需要实现 Cloneable然后重写 clone 方法。
finalize 方法
该方法和垃圾收集器有关系判断一个对象是否可以被回收的最后一步就是判断是否重写了此方
法。
equals 方法
该方法使用频率非常高。一般 equals 和 == 是不一样的但是在 Object 中两者是一样的。子类一
般都要重写这个方法。
hashCode 方法
该方法用于哈希查找重写了 equals 方法一般都要重写 hashCode 方法这个方法在一些具有哈
希功能的 Collection 中用到。
一般必须满足 obj1.equals(obj2)==true 。可以推出 obj1.hashCode()==obj2.hashCode() 但是
hashCode 相等不一定就满足 equals。不过为了提高效率应该尽量使上面两个条件接近等价。
- JDK 1.6、1.7 默认是返回随机数
- JDK 1.8 默认是通过和当前线程有关的一个随机数 + 三个确定值运用 Marsaglia’s xorshift
scheme 随机数算法得到的一个随机数。
wait 方法
配合 synchronized 使用wait 方法就是使当前线程等待该对象的锁当前线程必须是该对象的拥有者也就是具有该对象的锁。wait() 方法一直等待直到获得锁或者被中断。wait(long timeout)设定一个超时间隔如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态直到以下事件发生。
- 其他线程调用了该对象的 notify 方法
- 其他线程调用了该对象的 notifyAll 方法
- 其他线程调用了 interrupt 中断该线程
- 时间间隔到了。
此时该线程就可以被调度了如果是被中断的话就抛出一个 InterruptedException 异常。
notify 方法
配合 synchronized 使用该方法唤醒在该对象上等待队列中的某个线程同步队列中的线程是给抢占 CPU 的线程等待队列中的线程指的是等待唤醒的线程。
notifyAll 方法
配合 synchronized 使用该方法唤醒在该对象上等待队列中的所有线程。
总结
只要把上面几个方法熟悉就可以了toString 和 getClass 方法可以不用去讨论它们。该题目考察的是对 Object 的熟悉程度平时用的很多方法并没看其定义但是也在用比如说wait() 方法equals() 方法等。
Class Object is the root of the class hierarchy.Every class has Object as a
superclass. All objects, including arrays, implement the methods of this class.
大致意思Object 是所有类的根是所有类的父类所有对象包括数组都实现了 Object 的方法。
12.Java 创建对象有几种方式
new 关键字
平时使用的最多的创建对象方式
User user=new User();
反射方式
使用 newInstance()但是得处理两个异常 InstantiationException、IllegalAccessException
User user=User.class.newInstance();
Object object=(Object)Class.forName("java.lang.Object").newInstance()
clone方法
Object对象中的clone方法来完成这个操作
反序列化操作
调用 ObjectInputStream 类的 readObject() 方法。我们反序列化一个对象JVM 会给我们创建一个单独的对象。JVM 创建对象并不会调用任何构造函数。一个对象实现了 Serializable 接口就可以把对象写入到文中并通过读取文件来创建对象。
总结
创建对象的方式关键字new、反射、clone 拷贝、反序列化。
13.有了数组为什么还要再搞一个ArrayList呢
通常我们在使用的时候如果在不明确要插入多少数据的情况下普通数组就很尴尬了因为你不知道需要初始化数组大小为多少而 ArrayList 可以使用默认的大小当元素个数到达一定程度后会自动扩容。
可以这么来理解我们常说的数组是定死的数组ArrayList 却是动态数组。
14. 说说什么是 fail-fast
fail-fast 机制是 Java 集合Collection中的一种错误机制。当多个线程对同一个集合的内容进行操作时就可能会产生 fail-fast 事件。
例如当某一个线程 A 通过 iterator 去遍历某集合的过程中若该集合的内容被其他线程所改变了那么线程 A 访问集合时就会抛出 ConcurrentModificationException 异常产生 fail-fast 事件。这里的操作主要是指 add、remove 和 clear对集合元素个数进行修改。
解决办法建议使用“java.util.concurrent 包下的类”去取代“java.util 包下的类”。可以这么理解在遍历之前把 modCount 记下来 expectModCount后面 expectModCount 去和 modCount 进行比较如果不相等了证明已并发了被修改了于是抛出ConcurrentModificationException 异常。