Java面试题整理
1、Java基础
1、JDK 和 JRE 有什么区别?
JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。
2、简述Java的jvm虚拟机的运行原理
1)JVM是可运行Java代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域。JVM是运行在操作系统之上的,它与硬件没有直接的交互。
Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。
2)运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件),
再然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行
Java源文件—->编译器—->字节码文件
字节码文件—->JVM—->机器码
每一种平台的解释器是不同的,但是实现的虚拟机是相同的,这也就是Java为什么能够跨平台的原因了 ,当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不能共享。
参考:https://blog.csdn.net/yunzhaji3762/article/details/81038711
3、GC是什么? 为什么要有GC
Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,在使用JAVA的时候,一般不需要专门编写内存回收和垃圾清理代码。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。
电脑的内存大小的不变的,当我们使用对象的时候,如使用new关键字的时候,就会在内存中生产一个对象,但是我们在使用JAVA开发的时候,当一个对象使用完毕之后我们并没有手动的释放那个对象所占用的内存,就这样在使用程序的过程中,对象越来越多,当内存存放不了这么多对象的时候,内存不够就会导致溢出等错误导致程序崩溃.
JAVA为了解决这个问题就推出了这个自动清除无用对象的功能,或者叫机制,这就是GC,或者叫垃圾回收,其实就在用来帮你清理垃圾对象的,好让你安心写代码,不用管内存释放,对象清理的事情了。
Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,且Java语言没有提供释放已分配内存的显示操作方法。
垃圾回收只与内存有关,清理内存外的资源依靠finalize()方法。垃圾回收机制在回收某个对象的内存前会调用该对象的finalize()方法。
垃圾回收以单独的线程在后台运行,为减少虚拟机额外消耗,一般在内存不足时会进行垃圾回收,所以就算强制系统垃圾回收,垃圾回收也不一定发生
4、解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法;
通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;
而通过new关键字和构造器创建的对象放在堆空间;
程序中的字面量(literal)如直接书写的100、”hello”和常量都是放在静态区中。
栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用
的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。
5.说出一些常用的类,包,异常请各举5个
常用的类:BufferedReader BufferedWriter FileReader FileWirter String Integer Math Random Date
SimpleDateFormat Calendar System BigDecimal ...
常用的包:java.lang java.awt java.io java.util java.sql java.text
常用的异常:
RuntimeException Java.lang 包中多数异常的基类
ArithmeticException 算术错误,如除以 0
IllegalArgumentException 方法收到非法参数
IndexOutOfBoundsException 地址下标越界
NullPointerException 空指针异常
ClassCastException 类转换异常
NumberFormatException 数据格式异常
ClassNotFoundException 不能加载请求的类
IOException I/O 异常的根类
FileNotFoundException 不能找到文件
IllegalAccessException 对类的访问被拒绝
NoSuchMethodException 请求的方法不存在
InterruptedException 线程中断
SQLException SQL异常
6、switch是否能作用在byte上,是否能作用在long上,是否能作用在String上
switch(expr1)中,expr1是一个整数表达式。
因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。
现在1.7及更高版本中String可以用于swtich,还有枚举类型也支持
7、String s= new String(“abc”) 创建了几个String Object ?如果是多个,那么他们之间有什么区别?
两个或一个,“abc”对应一个对象,这个对象放在字符串常量池,
常量“abc”不管出现多少遍,都是缓冲区中的那一个。new String每写一遍,
就创建一个新的对象,它依据常量“abc”对象的内容来创建出一个新String对象。
如果以前就用过“abc”,这句代表就不会创建”abc”自己了,直接从缓冲区拿。
8、为什么说String是不可变的
String类被final修饰,不可被继承。
String的成员变量char数组value被final修饰,初始化后不可更改引用。
String的成员变量value访问修饰符为private,不对外界提供修改value数组值的方法。
由于String对象的内容(对象内存地址所存的内容)是不能改变的,但String引用(变量)是可以改变的,可以让其指向另外一个字符串。(不能修改字符串的内容,但可以修改字符串的引用)
String str = "hello";
str = str + "world";
System.out.println(str);// hello world
虽然str打印的结果变了,但不是String对象本身发生改变,而是str引用到了其他String对象(创建了一个新的字符串 "helloworld")。要区分修改的是"地址"还是"地址对应内存的内容"。
如果非要改,只能就借助反射进行修改
9、Java的访问修饰符是什么?
访问权限修饰符是表明类成员的访问权限类型的关键字。使用这些关键字来限定程序的方法或者变量的访问权限。
它们包含:
public: 所有类都可以访问
protected: 同一个包内以及所有子类都可以访问
默认: 归属类及相同包下的子类可以访问
private: 只有本类才能访问
10.方法重载和方法重写区别?
方法重载:同一个类中,方法名相同,参数的类型、顺序和个数不同,与返回值类型和方法访问修饰符无关
方法重写:不同类中,发生在继承类中,方法名称、参数类型、返回值类型全部相同,被重写的方法不能拥有比父类更严格的权限
11.Java实现多态的必要条件?
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法
12.抽象类和接口的区别?
抽象类 是一个不能被实例化的类,因为它其中存在抽象方法,但它的其它行为和一个普通类没什么不同。
接口是java为了弥补不能多继承提供的概念,接口之间支持多继承,接口中只允许存在公有静态常量或公有的抽象方法,一个类可实现多个接口,从而扩展不同的功能。java1.8之后,支持默认方法。
13.this、super、static、final关键字的用法
this:代表本类当前对象的引用,谁调用我,我就代表谁。
super:代表当前对象父类的内存空间标识。(可以理解为父类的引用,通过super可以访问父类的成员)
static:静态,可以修饰以下
变量:将实例变量升级为类变量,通过类名可以直接.出来
方法:将实例方法升级为类方法,通过类名可以直接.出来
代码块:静态代码块随着类的加载而加载,一般是用来加载驱动的。只在类加载的时候执行一次,优先于构造方法执行
final:最终的,可以修饰以下
变量:由变量变成常量,值不可修改
方法:表示此方法不能被重写
类:表示此类不能被继承
14、字符串对象String,StringBuilder ,StringBuffer之间的区别?
String是不可变对象,经常改变内容的字符串最好不要使用String,任何对String的改变都会引发新的String对象的生成
StringBuffer是可变的字符串,字符串经常改变的情况可使用StringBuffer,更高效,任何对它所指代的字符串的改变都不会产生新的对象。
JDK1.5后提供了StringBuilder,等价StringBuffer。作为一个简易替代,他们的原理和操作基本相同,区别在于StringBuffer支持并发操作,线性安全的,适合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用,而且大量字符操作时候,相对来说执行会快.
因此:
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
回顾扩容机制
15、对比HashMap和HashTable?
两者都是用key-value方式获取数据。它们之间有以下区别:
1)线程安全性不同
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
2)key和value是否允许null值
其中key和value都是对象,并且不能包含重复key,但可以包含重复的value。
Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
3)内部实现使用的数组初始化和扩容方式不同
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
扩展:在使用 HashMap 的时候,用 String 做 key 有什么好处?
HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,避免重复计算hashCode,所以相比于其他对象更快。
如何将类作为一个HashMap的key?
实现对象作为键需要重写hashCode()和equals()方法。
两个方法的作用参考:https://www.cnblogs.com/Qian123/p/5703507.html
16、ArrayList和LinkedList、Vector的区别?
1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2)对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3)对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据
Vector和ArrayList类似,但是是线程安全的,所以开销大,效率低, Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.
17、编写多线程程序有几种实现方式?
1) 一种是继承Thread类;
2) 一种是实现Runnable接口。
两种方式都要通过重写run()方法来定义线程的行为,推荐使用后者,因为Java中的继承是单继承,一个类有一个父类,如果继承了Thread类就无法再继承其他类了,显然使用Runnable接口更为灵活。
3)启动一个线程使用的是start方法,自动调用线程的run方法。
4)通过Callable和FutureTask创建线程,最大特点就是可以有返回值
5)通过线程池创建线程
18、sleep和wait的区别。
整体的区别其实是有四个:
1、sleep是线程中的方法,但是wait是Object中的方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。
参考:https://baijiahao.baidu.com/s?id=1647423693517849309&wfr=spider&for=pc
19、java如何停止一个线程
1)要终止一个线程,并不是简单的调用stop()方法,stop()就像linux中的kill一个线程一样是非常暴力的,虽然在Java的API中仍然可以调用stop()方法,但是和suspend,resume方法一样,是过期了的,不建议使用的,因为stop()在结束一个线程时并不会保证线程的资源正常释放,会导致程序可能会出现一些不确定的状态。
正确终止一个线程的方法有以下几种:
1)interrupt方法
当其他线程调用当前线程的interrupt方法时,即设置了一个标识,表示当前线程可以中断了,至于什么时候中断,取决于当前线程。当前线程通过调用isInterrupted()方法来判断自身是否被中断,如果返回true,则说明当前线程已经中断,这种通过标识位或中断操作的方式能够使线程在终止时有机会清理资源,而这种方式也更加安全。
2)volatile关键字
定义一个用volatile修饰的成员变量来控制线程的停止,这点是利用了volatile修饰的成员变量可以在多线程之间达到共享,也就是可见性来实现的,同一时刻只能由一个线程来修改该变量的值。
3)程序运行结束或者出现异常
当一个程序运行结束或者出现异常后,当前线程自然也就终止了
20、final、finally、finalize的区别。
1)final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。
2)finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
3)finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。
21、JVM调优,线程,同步锁等相关内容。
java面试汇总中的JVM篇,多线程&并发篇。
22、如何比较日期的大小,如何格式化日期
1)获取毫秒数,判断大小
2)使用Date类提供的方法after或者before方法
3)SimpleDateFormat的format方法可以格式化日期,格式为yyyy-MM-dd HH:mm:ss
4)java8的新用法:使用LocalDateTime
23、如何将字符串反转?
1)使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
2)将字符串转为字符数组,循环倒序输出
3)直接使用charAt方法从后往前输出
24、String 类的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
25、java 中 IO 流分为几种?
按流向来分:
输入流(InputStream/Reader)、输出流(OutputStream/Writer)。
按类型来分:
字节流(InputStream/OutputStream)和字符流(Reader/Writer)(非纯文本文件,不要用字符流,会导致文件格式破坏)。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
还可以划分为:
节点流(低级流直接跟输入输出源对接,如:FileInputStream/FileOutputStream/FileReader/FileWriter)和处理流(高级流建立在低级流的基础上,如:BufferedReader/BufferedReader可对节点流经行包装,使读写更快)
实际用法,回顾上课的代码案例
26、Collection 和 Collections 有什么区别?
1)java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
2)Collections则是集合类的一个工具类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
27、如何实现数组和 List 之间的转换?
1)List转换成为数组:调用ArrayList的toArray方法。
2)数组转换成为List:调用Arrays的asList方法。这种方法执行后,是不能再调用add方法的,这个要注意。
28、线程的五种状态
创建、就绪、运行、阻塞和死亡。
创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪
29、java反射的原理,作用,和用法
原理:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
作用:
在运行时根据一个对象,获取所属的类
在运行时获取任意一个对象所在类所具有的成员变量和方法
用法:
要想操作反射,必须先拿到反射的类对象,方法三种:
1)通过通过Class.forName("全类名")
2)类名.class
3)对象.getClass()
参考:https://blog.csdn.net/weixin_42724467/article/details/84311385
30、Java序列化的作用
序列化的原本意图是希望对一个Java对象作一下“变换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,
另外变换成字节序列也更便于网络运输和传播,所以概念上很好理解:
序列化:把Java对象转换为字节序列。
反序列化:把字节序列恢复为原先的Java对象。
序列化是不是靠Serializable接口实现的
不是,它仅仅只是做一个标记用!!!它告诉代码只要是实现了Serializable接口的类都是可以被序列化的!然而真正的序列化动作不需要靠它完成。否则异常NotSerializableException
serialVersionUID是序列化前后的唯一标识符
serialVersionUID序列化ID,可以看成是序列化和反序列化过程中的“暗号”,在反序列化时,JVM会把字节流中的序列号ID和被序列化类中的序列号ID做比对,只有两者一致,才能重新反序列化,否则就会报异常来终止反序列化的过程。
默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!
如果在定义一个可序列化的类时,没有人为显式地给它定义一个serialVersionUID的话,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID,一旦像上面一样更改了类的结构或者信息,则类的serialVersionUID也会跟着变化!
参考:https://blog.csdn.net/qq_32575047/article/details/80070590
2、前端(HTML+JS+JQuery)
1、页面导入样式时,使用link和@import有什么区别?
1)本质的差别:link属于XHTML标签,而@import完全是CSS提供的一种方式。
2)加载顺序的差别:当一个页面被加载的时候(就是被浏览者浏览的时候),link引用的CSS会同时被加载,而@import引用的CSS会等到页面全部被下载完再被加载。所以有时候浏览@import加载CSS的页面时开始会没有样式(就是闪烁),网速慢的时候还挺明显。
3)兼容性的差别:@import是CSS2.1提出的,所以老的浏览器不支持,@import只有在IE5以上的才能识别,而link标签无此问题。
4)使用dom(document o bject model文档对象模型 )控制样式时的差别:当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的.