java基础面试题
1.请说说Java的特点和优点,为什么要选择Java:
- Java是一门非常纯粹的面向对象的编程语言
- Java的特点:Java省去了C++的多继承,指针等
- Java的优点:1.面向对象,2.平台无关性,3.简单性,4.解释执行,5.多线程:6.分布式,7.健壮性,8.高性能:9.安全性
2.对面向对象的理解:
- 面向对象三大基本特性:1.封装,2.继承,3.多态
- 封装:将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,让外部程序通过该类提供的方法来实现内部信息的操作和访问,提高了代码的可维护性。
- 继承:通过extends实现类的继承,实现代码复用的重要手段。
- 多态:父类变量指向子类实例
3.Java8的新特性:
- Lambda表达式:可以更简洁地表示单方法接口的实例
- 方法引用:可以直接引用已有Java类或对象(实例)的方法或构造器,方法引用使用一对冒号 :: ,可以代码更加紧凑简洁
- 默认方法:允许在接口中定义默认方法,默认方法必须使用default修饰
- Stream API:把真正的函数式编程风格引入到Java,可以对集合进行批量操作
- Data Time API:加强对日期与时间的处理
4.包装类的自动拆箱和自动装箱:
- 包装类的的作用:是解决Java的八种基本数据类型不面向对象的缺陷,有了包装类,所有的类都可以继承object类。
- 自动装箱,自动拆箱是JDK1.5提供得到功能
- 自动装箱:基本类型的数据和变量可以直接赋值给包装类的变量
- 自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量
5.String是不可变类型还是可变类型,为什么不可变:
- 答1:不可变类型
- 答2:String被final修饰,被创建的对象只能被赋值一次
6.String和new String()的区别:
- 答1:String str1="abc" 首先查看常量池是否有“abc”这个对象,存在着返回地址,不存在则创建“abc”对象并返回地址。
- 答2:String str1=new String("abc") 不管字符串池中是否存在字符串"abc",直接新建一个字符串"abc
7.String,StringBuffer,StringBuilder区别:
- String是只读字符串
- StringBuffer/StringBuilder是Java提供的两个类来封装字符串,并提供了一些修改字符串修改的方法
- StringBuffer和StringBuilder的方法完全相同
- 区别在于StringBuilder是单线程下使用,它的所有方法都被有被synchronized修饰
8.final, finally, finalize 的区别:
- final意为最终,用来修饰java的类(不可继承),方法(不能被重写),变量(只能被赋值一次)
- finally属于异常处理try-catch,finally一定会在return之前执行,但如果finally中出现return或者throw,会使try-catch中的return或者catch实效。
- finalize()是Java中Object的一个方法,是在垃圾收集器删除对象之前对这个对象调用
9.反射的过程和作用:
- 过程:反射就是java文件编译成字节码文件后,通过字节码文件找到一类中的成员
- 作用:反射机制是指程序在运行时能获取对象的属性和方法的功能
- 获取Class对象的三种方式:
- .getClass(),2. .class,3. Class
- 优缺点:
- 运行期间能够动态的获取类,提高代码得到灵活性
- 性能比直接的Java代码要慢
- 应用场景:
- 1.spring的xml配置模式,2.动态代理模式
10.请写出jdbc连接过程:
//导入jar包
Class.forName("com.mysql.jdbc.Driver");
//注册驱动
Connection con=DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm?userSSL=false","root","");
//获取数据库连接对象
Statement stmt=con.getcreatStatement();
//执行sql语句,DML语句
int count=stmt.executeUpdate(sql);
//执行sql语句,DQL语句
Result re=stmt.executeQuery(sql);
while(rs.next()){
int a=rs.getInt("id");
.....
}
11.wait()和sleep()的区别:
- 所属的类型不同:
- wait() 是Object类的实例方法,调用该方法的线程将进入waiting状态
- sleep() 是Thread类的静态方法,调用该方法的线程将进入TIMED_WAITING状态
- 对锁的依赖不同:
- wait() 依赖于synchronized锁,通过监视器(Monitor)进行调用后线程会释放锁
- sleep() 不依赖于任何锁,所以在调用后它也不会释放锁
- 返回的条件不同:
- 调用wait() 进入等待状态的线程,需要由notify(),notifyAll() 唤醒,从而返回
- 调用sleep() 进入超时等待的线程,需要在超时时间到达后自动返回
12.基本数据类型和引用数据类型的区别:
- 基本数据类型:是对值得直接操作,它主要分配在栈中
- 引用数据类型:它得引用在栈中,对象得创建在内存堆中,引用指向堆中的地址;引用数据类型默认继承于Object基类
13.equals和==的区别:
- ==:如果是基本类型比较的数值,如果是引用类型比较的是地址值
- equals:用来比较两个对象的内容是否相等
14.HashCode() 和equals() 的区别,为什么重写equals() 就要重写hashCode():
- HashCode() 方法的主要用途是获取哈希码,equals() 方法主要用来比较两个对象内容是否相等
- HashCode() 和equals() 的约定:如果两个对象相等,他们必须有相同的哈希码;equals() 比较两个对象相等时HashCode() 一定相等
- 重写的原因:由于HashCode() 和equals() 具有联动关系,equals重写时,HashCode() 进行重写使得这两个方法始终满足相关的约定
15.接口和抽象类的区别:
- 抽象类:1.可以定义构造器,2.可以有抽象方法也可以有具体方法,3.抽象类的权限修饰符可以是多种,4.抽象类可以定义成员变量,5.抽象类可以包含静态方法,6.一类只能继承一个抽象类
- 接口:1.不能定义构造器,2.方法全部是抽象方法,3.接口的成员全部都是public的,4.定义的成员变量实际是常量,5.不能有静态方法,6.一个类可以实现多个接口
16.重载和重写的区别:
- 重载:
- 重载发生在同一类中
- 要求方法名必须相同,参数列表不同
- 并且和方法返回值和访问修改符无关
- 重写:
- 重写发生在父子类中
- 子类重写方法的名称和参数列表必须和父类相同
- 子类的返回值类型必须是父类的返回值及子类型,抛出异常类型必须是父类的异常类型及子类型
- 子类重写方法的访问修饰符必须大于父类,若父类方法的修饰符为private则子类不能重写该方法
17.线程的状态:
- 初识状态(new):new一个线程就是创建状态
- 运行状态(runnable):
- 就绪:执行start方法就是就绪状态
- 运行:处于就绪状态的线程获取了cpu之后就是运行状态
- 阻塞状态(blocked):线程正在等待获取监视器锁
- 等待状态(waiting):线程正在等待其他线程的通知或中断
- 超时等待状态(timed_waiting):在等待的基础上增加了超时时间,超出时间自动返回
- 终止状态(terminated):线程终止运行,最后的run() 方法执行完或者抛出一个异常时就销毁了。
18.线程的生命周期状态:
- 1.新建,2.就绪,3.运行,4.堵塞,5.死亡
19.创建线程的方法:
- 1.继承Thread类,2.实现Runnable接口,3.实现callable接口,4.使用线程池或者Executor框架
20.请你说说多线程:
- 线程和进程的关系:
- 线程是操作系统调度的最小单元,
- 一个进程可以拥有多个线程
- 多线程的共享空间和资源:
- 各个线程都拥有各自的程序计数器,虚拟机栈,本地内存栈,并且能够共享进程内的资源(方法区和堆)
- 多线程的优点:
- 1.减少程序响应时间,2.提高CPU利用效率,3.创建和切换开销小,4.数据共享效率高,5.简化程序结构
21.线程安全:
-
线程安全问题:多个线程同时操作统一共享资源的时候可能会出现业务安全问题
-
线程安全:有多个线程同时运行,而多个线程同时运行这段代码,多线程下运行的结果和单线程一致就为线程安全
-
线程同步:
-
主要通过加锁的方式实现线程同步
-
方式一:synchronized同步代码块,方式二:synchronized同步方法,方式三:Lock锁、
-
22.线程通信的方式:
- 线程通信的定义:线程间互相发送信息
- 常见的线程通信方式:1.利用Monitor实现线程通信,2.利用Condition实现线程通信
- 使用不同的线程同步方式也就对应的使用不同的线程通信方式
- 使用synchronize同步时就会使用monitor来实现线程通信,使用Lock进行同步时就是使用Condition来实现线程通信
23.线程池的理解:
- 线程池就是一个可以复用线程的技术
- 不使用线程池:如果用户每发起一个请求,后台就创建一个线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的。这样严重影响系统的性能
- 核心参数:
- 1.corePoolSize( 核心线程数 ),2.workQueue( 等待队列 ),3.maxinumPoolSize( 最大线程数 ),4.handler( 拒绝策略 ),5.keepAliveTime( 空闲线程存活时间 )
24.对ThreadLocal的理解:
- ThreadLocal即线程变量
- 作用:它用于共享变量在多线程中的隔绝,即每个线程都有一个该变量的副本彼此互不影响也就不需要同步机制了
- 实现原理:每个Thread对象中都有一个ThreadLocal类的内部类ThreadLocalMap对象,它是一个键值形式的容器,以ThreadLocal对象的get和set方法来存取共享变量值
25.G1垃圾回收器:
- G1收集器是一个多线程的采用标记清理算法的,面向混合收集(同时收集新生带和老生带)的一个垃圾收集器
- G1收集器将整个堆内存区域划分为多个大小相等的region,以region为单位进行垃圾收集并获取每个region的收集效率和收集收益
- G1垃圾收集器回收主要包括四个流程:
- 初始标记
- 并发标记(类似CMS)
- 最终标记在并发标记中维护了一个remember set log 用来记录在该阶段发生变化的对象引用关系
- 并发筛选回收
26.内存溢出:
- 内存溢出:指程序运行过程中申请的内存大于系统能够提供地内存,导致无法申请到足够的内存的情况
- 引发内存溢出的原因:1.内存加载的数据量过于庞大,2.代码中存在死循环或者死循环中产生大量的对象实体,3.启动内存值设定过小
- 常见的内存溢出情况:
- 堆:对象创建过多,2.栈溢出,3.方法区和运行时常量池
- 解决内存溢出的方案:
- 修改JVM启动参数,直接增加内存
- 检查错误日志,查看“OutOfMemory”错误之前是否存在异常
- 对代码进行debug分析
- 使用内存工具动态查看内存使用情况
27.内存泄露:
- 内存泄露:是指不再使用对象仍然内引用,导致垃圾收集器无法回收它们的内存,由于不再使用的对象仍然无法清理,甚至这种情况可能越积越多,最终导致致命的OutOfMemoryError
28.对Java集合的了解:
- Java中的集合类主要都有Collection和Map这个接口派生而生
- 所有的集合类都是List,Set,Queue,Map这四个接口实现
- Collection接口:
- List接口:有序的,可重复的数据集合
- ArrayList实现类:
- LinkedList实现类:
- Set接口:无序的,不可重复的数据集合
- HashSet实现类:底层采用哈希表存储的数据,
- TreeSet实现类:基于红黑树的数据结构实现排序
- Queue接口:先进先出的队列
- List接口:有序的,可重复的数据集合
- Map接口:是具有映射关系的集合
- HashMap实现类:
- HashTable实现类:
- .......
29.线程安全的集合:
- 古老的集合(已经不推荐使用):1.List接口下的vector实现类,2.Map接口下的Hashtable实现类(都是基于Synchronized实现)
- 对于非安全实现类:使用Collections工具类的sychronized方法转换为线程安全的集合
- jdk1.5之后使用concurrent包下支持大量并发访问的集合类,例:ConcurrentHashMap / CopyOnWriteArrayList
30.JUC的了解:
- JUC是java.util.concurrent的缩写,这个包中包含了支持并发操作的各种工具
- 原子类:遵循比较和替换原则,可以用于解决单个变量的线程安全问题
- 锁:与Synchronized类似,在包括Synchronized所有功能的基础上,还支持超时机制,响应中断机制
- 线程池:可能更方便的管理线程,同时避免重复开线程和杀线程带来的消耗,效率高
- 并发容器:例如ConcurrentHashMap,支持多线程操作的并发集合,效率更快
31.HashMap的底层原理:
- 数据结构:“数组+链表+红黑树”
- put()流程:(基于哈希算法来确定元素位置)计算传入key的哈希值,并利用哈希值绝对值再余除集合长度来确定元素位置
- 如果这个位置,已经存在其他元素就发生哈希碰撞;HashMap使用链地址法解决哈希冲突,并且当链表长度为8时,链表会转化为红黑树,提高查询效率
- 扩容机制:数组的默认容量是16,会以2的指数倍进行扩容
- 线程安全:Hashmap非线程安全,在多环境下会产生循环死链;因此多线程环境下建议使用ConcurrentHashmap
32.ConcurrentHashMap集合
- 支持大量并发访问的线程安全集合,底层和HashMap一样采用“数组+链表+红黑树”
- 采用锁定头节点的方式降低了锁粒度,以较低的性能实现了线程啊安全
33.HashMap和HashTable的区别:
- HashMap:
- 是非线程安全
- 允许存入null的,无论是以null作为key或value,都是可以的
- HashTable:
- 是线程安全,性能不如HashMap
- 不允许存入null值,无论是以null作为key或value,都会引发异常
- 多线程环境下,不建议使用HashTable,建议使用java.util.Concurrent下的ConcurrentHashMap,它不但保证了线程安全,也通过降低锁的粒度提高了并发访问时的性能
34.对JVM的了解:
- JVM是java语言跨平台的关键
- HotSpot是Sun / Oracle JDK / Open JDK的默认Java虚拟机,也是目前使用范围最广的Java虚拟机
- JVM由三部分组成:1.类加载子系统,2.执行引擎,3.运行时数据区
35.JVM内存模型
-
java虚拟机,简称JVM,是运行所有java程序的假象计算机,是java程序的运行环境,是java最具吸引力的特性之一。我们编写的java代码,都运行在JVM之上
-
JRE(java Runtime Environment):是java程序的运行环境,包含JVM和运行时所需要的核心库(运行)
-
JDK(java Development Kit):是java程序开发工具包,包含JRE和开发人员使用的工具(开发)
-
包含关系:
-
JVM由三部分组成:1.类加载子系统,2.执行引擎,3.运行时数据区
-
类加载子系统:可以根据全限定名来载入类或接口
-
执行引擎:负责执行那些被加载的类中的指令
-
运行时数据区:内存中存储数据
- 方法区:
- 堆:
- 虚拟机栈:
- 本地方法栈:
- 程序计数器:
36.JVM的垃圾回收机制:
- JVM采用分代垃圾收集理论,分代收集建立在三个分代假说之上,即弱分代假说,强分代假说,跨分代引用假说
- 依据分代假说理论,垃圾回收可以分为如下几类:
- 新生代收集:目标为新生代的垃圾收集
- 老年代收集:目标为老年代的垃圾收集,只有CMS收集器会有这种行为
- 混合收集:目标为整个新生代及部分老年代的垃圾收集,目前只有G1收集器会有这种行为
- 整堆收集:目标为整个堆和方法区的垃圾收集
37.类加载机制:
-
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止;
-
它的整个生命周期将会经历加载,验证,准备,解析,初始化,使用,卸载七个阶段
-
其中验证,准备,解析三个部分统称为连接,而前五个阶段则是类加载的完整过程
38.Java中常见的锁及原理:
- synchronized关键字:底层采用Java对象头来存储锁信息的
- Lock锁接口:基于AQS实现,AQS内部定义一个先进先出的队列实现锁的同步,同时还定义了同步状态来记录锁信息。
39.synchronize的用法及原理:
- 能够保证同一个时刻只有一个线程执行该代码,保证线程安全。在执行完或者出现异常时自动释放锁
- 用法:
- 静态方法:则锁是当前类的Class对象
- 普通方法:则锁是当前的实例(this)
- 代码块:则需要在关键字后面的小括号里,显式指定一个对象作为锁对象
- 原理:底层是采用Java对象头来存储锁信息的,并且还支持锁升级
40.AQS队列同步器:
- AQS是(AbstractQueuedSynchronizer)队列同步器,是用来构建锁的基础框架
- AQS是基于模板方法模式进行设计的,Lock实现类都是基于AQS实现的,所以锁的实现需要继承AQS并重写它的方法
- AQS内部定义了一个FIFO的队列来实现线程的同步,同时还定义了同步状态来记录锁的信息。
- 它的线程模式:独占和共享模式
41.synchronized和Lock有什么区别:
- synchronized:是java关键字
- Lock:lock是一个接口
- 使用方式:
- synchronized:隐式锁,自动释放锁,在这种同步状态下,我们需要依赖Monitor(同步监视器)来实现线程通信
- 作用在静态方法上,类的Class对象
- 作用在实例方法上,当前实例(this)
- 作用在代码块上,则需要在关键字后面的小括号里显式指定一个对象作为Monitor
- Lock接口是显示锁,手动释放,Lock接口具备更大的灵活性
- synchronized:隐式锁,自动释放锁,在这种同步状态下,我们需要依赖Monitor(同步监视器)来实现线程通信
- 功能特性:Lock弥补了synchronized不足,它新增特性包括:1.可中断地获取锁,2.非阻塞地获取锁,3.可超时地获取锁
- 实现机制:
- synchronized的底层是采用Java对象头来存储锁信息的
- lock通过trylock查看是否加锁成功
42.volatile的了解
- volatile关键字是比synchronized关键字更加轻量化,只能保证单共享变量的线程安全问题
- 保证变量的可见性和有序性,1.可见性:当一个共享变量被修改时,其他线程可以知道,2.有序性:通过禁止指令重排实现
43.BIO,NIO,O的了解:
- UNIX提供了5种I/O模型:1.阻塞I/O模型,2.非阻塞I/O模型,3.I/O复用模型,3.信号驱动I/O模型,异常I/O模型
- BIO:阻塞I/O模型(blocking I/O),是最常见的I/O模型,缺省情形下,所有文件操作都是阻塞的,一线程只能操作一个IO
- NIO:非阻塞I/O模型(nonblocking I/O),IO多路复用,一个线程可以同时处理多个IO请求,提供select,poll,epoll调用
- O:异步I/O模型(asynchronous I/O),告知内核启动某个操作,并让内核在整个操作完成后通知我们
44.Java NIO:
- NIO:非阻塞I/O模型(nonblocking I/O),IO多路复用,一个线程可以同时处理多个IO请求,提供select,poll,epoll调用
- NIO包含三大核心的组件:1.Buffer( 缓存区 ),2.Channel( 通道 ),3.Selector( 多路复用器 )
45.IO多路复用:
- IO多路复用:单个线程同时操作多个IO请求
- select调用:查询有多少个文件描述符需要进行IO操作,特点:1.轮询次数多,2.内存开销大,3.支持文件描述符的个数有限
- poll调用:和select几乎差不多,但是它的底层数据结构为链表,所以支持文件描述符的个数无上限
- epoll调用:更加高效的调用方式,底层的数据结构为红黑树加链表
46.static修饰符的用法:
- Java类中包含了成员变量,方法,构造器,初始化块和内部类(包括接口,枚举)5种成员
- static可修饰类,方法,代码块,变量
- 静态类:可以在不创建实例的情况下访问它的静态方法或者静态变量,而其实例方法或实例成员变量只能通过其实例对象来访问
- 静态方法:在静态方法不能使用this,因为this是随着对象创建而存在
- 静态成员变量:静态成员变量随着静态类的加载而创建
47.Java的四种引用方式:
- 强引用:以new关键字创建的引用都是强引用,被强引用的对象永远都不会被回收
- 软引用:以softReference引用对象,被弱引用的对象只有内存空间不足时会被垃圾回收
- 弱引用:以WeakReference引用对象,被弱引用引用的对象一定会被回收,它只能存活到下一次垃圾回收
- 虚引用:以PhantomReference引用对象,一个对象被引用后不会有任何影响,也无法通过该引用来获取该对象,只是其再被垃圾回收时会收到一个系统通知。
48.泛式和泛式擦除:
- 泛型的本质:参数化类型,即给类型指定一个参数
- 是JDK1.5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
- 泛式的格式:<引用数据类型>
- 泛式的使用:1.泛式接口,2.泛式类,3.泛式方法
- 泛式的好处:1.可以在在编译时检查类型安全,2.所有的强制转换都是自动和隐式的,3.可以提高代码的重用率
- 泛式擦除:编译器在编译期间将我们写好的泛型进行擦除,并相应的做出一些类型转换
49.Java的异常处理机制:
- 异常处理机制可以让程序具有极好的容错性和健壮性,系统会生成一个Exception对象来通知程序
- 处理异常的语句由try,catch,finally三部分组成,try块用于包裹业务代码,catch块用于捕获并处理某一个类型的异常,finally块则用于回收资源
- 过程:如果业务代码发生异常,系统创建一个异常对象,并将其提交给JVM;由JVM寻找可以处理这个异常的catch块,并将异常对象交给这个catch块;如果JVM没有找到,运行环境终止,Java程序退出。
- Java也允许程序主动抛出异常。当业务代码中,判断某项错误的条件成立时,可以使用throw关键词向外抛出异常
50.访问修饰符:
51.类的实例化过程:
- 过程:1.类加载,2.分配内存,3.初始化零值,4.状态设置,5.构造函数