java基础面试题
一、基础
1.Java八大基本数据类型
byte short int long
float double
char boolean
不同修饰符的范围:
不同修饰符的范围2.一个.java源文件中是否可以包含多个类(不是内部类),有什么限制?
可以有多个类,但只能有一个public的类,并且public的类名必须与文件名一致
3.静态变量和实例变量的区别?
在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。
在程序运行时的区别:
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,
静态变量就会被分配空间,静态变量就可以被使用了。
4.构造器Constructor是否可被override?
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
5.写clone()方法时,通常都有一行代码,是什么?
clone 有缺省行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
6.类中各个成员的执行顺序
类加载的时候,首先初始化静态属性,再执行静态代码块;
当new一个对象时,先执行构造代码块({}),再执行构造方法。
以上两种,如果存在继承,都是先执行父类的,再执行子类的。
class Parent {
public static int t = parentStaticMethod2();
{
System.out.println("父类非静态初始化块");
}
static{
System.out.println("父类静态初始化块");
}
public Parent(){
System.out.println("父类构造方法");
}
public static int parentStaticMethod(){
System.out.println("父类类的静态方法");
return 0;
}
private static int parentStaticMethod2(){
System.out.println("父类类的静态方法2");
return 9;
}
}
class Child extends Parent {
public static int t = childStaticMethod2();
{
System.out.println("子类非静态初始化块");
}
static {
System.out.println("子类静态初始化块");
}
public Child(){
System.out.println("子类构造方法");
}
public static int childStaticMethod(){
System.out.println("子类的静态方法");
return 1000;
}
public static int childStaticMethod2(){
System.out.println("子类的静态方法2");
return 1000;
}
}
public class MyTest {
public static void main(String[] args) {
Parent child = new Child();
}
}
```
打印结果:
父类类的静态方法2
父类静态初始化块
子类的静态方法2
子类静态初始化块
父类非静态初始化块
父类构造方法
子类非静态初始化块
子类构造方法
7.String 和StringBuilder、StringBuffer 的区别
String:不可变字符序列
StringBuffer:可变字符序列,效率低,线程安全
StringBuilder:可变字符序列,效率高,线程不安全
8.String s = new String("xyz");创建了几个String Object
2个,一个是编译时决定的,最后放在常量池中。一个是运行时放在堆里面的。两个都是"xyz"
9.String str = "abc"和new String("abc")的区别:
String str = "abc":
这种方式创建字符串的时候,jvm首先会检查字符串常量池中是否存在该字符串对象:
如果已经存在,那么就不会在字符串常量池中再创建了,直接返回该字符串常量池中的内存地址;
如果该字符串不存在于字符串常量池中,那么就会在字符串常量池中先创建该字符串对象,然后再返回。
new String("abc"):
这种方式会创建字符串的时候,jvm首先会检查字符串常量池中是否存在"abc"字符串,如果已经存在,则不会在字符串常量池中创建了;
如果还没有存在,那么就会在字符串常量池中创建"abc"字符串对象,然后还会去到堆内存中再创建一份字符串的对象,把字符串常量池中的"hcx"字符串内容拷贝至内存中的字符串对象,然后返回堆内存中字符串对象的内存地址。
10.Java创建对象的几种方式
1.使用new语句创建对象(调用构造方法)
2.使用反射手段(调用构造方法)
3.调用对象的clone()方法(不调用构造方法)
4.运用反序列化(不调用构造方法)
>注意:第三种方式和是对已有对象的影印,所有不会调用构造方法,第四种是从文件中还原类的对象,也不会调用构造方法。
11.创建class对象的三种方式(获取类的字节码文件)
1.Class.forName()
2.类名.class
3.对象.getClass()
12.反射机制是什么
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能调用它的任意一个属性和方法
这种动态获取的信息以及动态调用对象的方法的功能称为java的反射机制
13.反射机制能做什么
在运行时判断任意一个类所具有的成员变量和方法
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时调用任意一个对象的方法
生成动态代理
14.类加载器的机制(ClassLoader)
classLoader用来动态加载class文件到内存中
java默认提供三个classloader:
1.Bootstrap classloader:启动类加载器,是java类加载器中最顶层的类加载器,负责加载jdk中的核心类库:rt.jar、resource.jar、charset.jar
2.Extension classloader:扩展类加载器,负责加载java的扩展类库,加载java的扩展类库,默认加载java_home/jre/lib/ext/目录下的所有java文件
3.App classloader:系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。
15.==和equals的区别:
==:比较两个变量的值和内存地址是否相同
Equals:只判断字符串内容是否相同,不判断地址。
Object的equals方法和==是一样的,没有区别。
而String类、Integer类等一些类,重写了equals方法,才使得equals和==不同,
所以自己创建类时,自动继承了Object的equals方法,要想实现不同的等于比较,必须重写equals方法。
==比equals运行速度快
16.final, finally, finalize的区别?
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally 是异常处理语句结构的一部分,表示总是执行。
finalize 是Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
17.接口和抽象类的区别:
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
4. 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
7. 一个类可以实现多个接口,但只能继承一个抽象类。
18.拦截器和过滤器的区别:
二、异常
1.异常体系
Throwable:所有错误或者异常的父类
--------------Error:错误
--------------Exception:异常
-------------------运行时异常:一个方法内部抛出了一个运行时异常,那个方法可以声明也可以不声明,调用者可以处理也可以不处理。
-------------------编译时异常(非运行时异常,受查异常):一个方法内部抛出了一个编译时异常对象,那么方法就必须声明,而且调用者也必须要处理。
2.Error和Exception的区别:
Error:是系统中的错误,是在程序编译时出现的错误,只能通过修改程序才能修正。
一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。
Exception(异常):表示程序可以处理的异常,可以捕获且可能恢复。
遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
3.5 种常见到的runtime exception
空指针异常:NullPointerException:当操作一个空引用时会出现此错误。
数据格式转换异常:NumberFormatException:数据格式转换出现问题时出现此异常。
强制类型转换异常:ClassCastException:强制类型转换类型不匹配时出现此异常。
数组下标越界:ArrayIndexOutOfBoundsException:数组下标越界,当使用一个不存在的数组下标时出现此异常。
ArithmeticException:数学运行错误时出现此异常
4.线程的生命周期:
1.新建和就绪 2.运行和阻塞 3.线程死亡
5.死锁的条件(四个条件同时满足)
1.互斥等待:同一个操作,同时只能有一个人操作,很多个人都想做时,第一个人抢到了锁可以进行操作,而其他没有抢到锁的人必须等待他做完才能做。
2.hold and wait (拿着一个锁,去等待另一个锁)
3.循环等待 (我拿了a的锁去等待b,另一个人拿着b的锁去等待a)
4.无法剥夺的等待(等了就一直等下去)有一些会有超时的设置,这种情况不会一直死锁。
解决:
1.破除互斥等待:一般无法破除
2.破除hold and wait:一次性获取所有资源(想办法让两个锁同时拿到,在拿到第一个锁的时候,设置一个很短的超时,让第二个锁也拿到,如果在一定的时间内无法拿到第二个锁,则释放掉第一个锁;或者使用全局锁,但效率不高)
3.破除循环等待:按顺序获取资源(让不同的线程按照一定的顺序执行,不要同时执行。)
4.无法剥夺的等待:加入超时。(等到一定的时间就超时,操作失败,用户体验不好)
线程池的参数:
corePoolSize:线程池中初始线程数量,可能处于等待状态
maximumPoolSize:线程池中最大允许线程数量
keepAliveTime:超过corePoolSize部分线程如果等待这些时间将被回收。
三、集合
1.集合体系
Collection:单列集合的根接口
-----List:有序,可重复
-----------ArrayList:查询快,增删慢(底层维护了一个Object数组)
-----------LinkedList:查询慢,增删快(底层使用了链表数据结构实现)
-----------Vector:与ArrayList一样,但是是线程安全的,操作效率低。
-----Set:无序,不可重复
-----------HashSet:存取速度快(底层用哈希表支持)
-----------TreeSet:如果元素具备自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
>注意:ArrayList和Vector都有一个初始的容量大小,当存储进去它们里面的元素个数超出容量的时候,就需要增加ArrayList和Vector的存储空间,每次增加存储空间的时候不是只增加一个存储单元,是增加多个存储单元。
Vector默认增加原来的一倍,ArrayList默认增加原来的0.5倍。
Vector可以由我们自己来设置增长的大小,ArrayList没有提供相关的方法。
双列集合
Map:以键值对形式存在,键不可重复,值可重复。
------HashMap:底层基于哈希表,允许一个null键和多个null值
------TreeMap:基于二叉树数据结构实现的,会对元素的键进行排序存储
------Hashtable:实现方式和hashmap一样,但是hashtable是线程安全的,操作效率低,不允许null键和null值。
2.遍历map集合的三种方式:
map集合遍历3.GC是什么? 为什么要有 GC?
Java 提供的GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,
Java 语言没有提供释放已分配内存的显式操作方法。
在堆中,找到已经无用的对象,并把这些对象占用的空间收回使其可以重新利用。
要请求垃圾收集,可以调用下面的方法之一:
System.gc() 或Runtime.getRuntime().gc() 。
算法思路:
把所有的对象组成一个集合,或者可以理解为树状结构,
从树根开始找,只要可以找到的都是活动对象,如果找不到就应该被回收了。
四、多线程
1.一个java应用程序至少有几个线程?
至少有两个线程, 一个是主线程负责main方法代码的执行,一个是垃圾回收器线程,负责了回收垃圾。
2.停止一个线程
1. 停止一个线程 我们一般都会通过一个变量去控制的。
2. 如果需要停止一个处于等待状态下的线程,那么我们需要通过变量配合notify方法或者interrupt()来使用。
notify():被唤醒线程不会收到任何异常 只是唤醒线程池中的一个 ,不能指定唤醒哪一个
interrupt():可以没有锁调用,会受到InterruptedException异常,可以指定清除具体的线程
3.sleep()和wait()有什么区别?
sleep()方法:
sleep()方法是Thread类中的静态方法,当一个线程调用sleep()方法以后,不会释放同步资源锁,其他线程仍然会等待资源锁的释放。
wait()方法:
wait()方法是Object类提供的一个普通方法,而且必须同同步资源锁对象在同步代码块或者同步方法中调用。当调用wait()方法后,当前线程会立刻释放掉同步锁资源。其他线程就有机会获得同步资源锁从而继续往下执行。
4.谈谈对线程池的理解?jdk提供了哪几种线程池?他们有什么区别?
线程池可以提高线程的创建和销毁的开销
jdk提供了以下几种线程池:
- newSingleThreadExecutor(单线程的线程池)
只有一个线程在执行,相对于单线程执行任务
- newFixedThreadPool(固定大小的线程池)
固定线程数处理任务;当任务过多,则固定的线程数谁先执行完任务,就执行剩余任务
- newScheduledThreadPool(可缓存的线程池)
如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
5.synchronized和volatile的区别:
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是
立即可见的。
2)禁止进行指令重排序。
volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。