Java基础面试题
Equals与==的区别
使用==比较原生类型如:boolean,,int,char等等, 使用equals()比较对象.
1. ==是判断两个变量或类型是不是指向同一个内存空间,
equals是判断两个变量或实例所指向的内存空间的值是不是相同.
2. ==是指对内存地址进行比较,equals()是对字符串的内容进行比较
3. ==指引用是否相同, equals()指的是值是否相同.
自动拆装箱
自动装箱是将一个Java定义的基本数据类型赋值给相应封装类的变量,
拆箱与装箱是相反的操作,自动拆箱则是将一个封装的变量赋值给相应基本数据类型的变量.
Object有哪些工公用方法
Object是所有类的父类,任何类都默认继承Object
Equals在object中与==是一样的,子类一般需要重写该方法
hashCode 该方法用于哈希查找,重写了equals方法,一般都需要重写HashCode方法,这个方法在一些具有哈希功能的Collection中用到.
getClass 获得运行时类型
toString方法,转换成字符串,一般子类都有重写.否则会打印句柄(内存中的地址)
Java的四种引用
从JDK 1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期,这四种级别有高到低依次为:强引用, 软引用, 弱引用和虚引用.
- 1. 强引用
最普遍的一种引用方式,如String s = “abc”;变量s就是字符串”abc”的强引用,只要强引用存在,则垃圾回收器,就不会回收这个对象
- 2. 软引用
用于描述还有用,但非必须的对象,如果内存足够,不回收,如果内存不足,则回收,一般用于实现内存敏感的告诉缓存,软引用可以和引用队列 联合使用,如果然软引用的对象被垃圾回收,Jvm就会把这个软引用加入与之关联的引用队列中
- 3. 弱引用
弱引用和软引用大致相同,弱引用于软引用的区别在于:只具有弱引用的对象,拥有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有软引用的对象,不管当前内存空间足够与否,都会回收它的内存
- 4. 虚引用
就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的声明周期,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,虚引用主要用来跟踪对象被垃圾回收器回收的活动.
虚引用与软引用和弱引用的一个区别在于
虚引用必须和引用队列联合使用,当垃圾回收器准备回收一个对象是,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中.
HashCode的作用
- 1. HashCode的特性
① HashCode的存在主要是用于查找的快捷性.如Hashtable,HashMap等,HashCode经常用于确定对象的存储地址
② 如果两个对象相同,equals方法一定会返回true,并且这两个对象的HashCode一定相同
③ 两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能说明这两个对象在一个散列存储结构中
④ 如果对象的equals方法被重写,那么对象的HashCode也尽量重写
- 2. HashCode作用
Java中的集合有两类,一个是List一个是Set,前者集合内元素是有序的,元素可以重复,后者元素无需,但元素不可重复
Equals方法可用于保证元素不重复,但如果每增加一个元素就检查一次,若集合中现在已经有1000个元素,那么第1001个元素加入集合是,就要调用1000次equals方法,这显然会大大降低效率,所以,Java采用了哈希表的原理
这样一来,当集合要添加新的元素是,先调用这个元素的HashCode方法,就一下子能定位它应该放置的物理位置上
① 如果这个位置上没有元素,就可以直接存储在这个位置上,不用再进行任何比较了
② 如果这个位置上已经有元素了,就调用equals方法与新元素进行比较,相同的话就不存了
③ 不相同的话,也就是发生了Hash Key相同导致冲突的情况,那么这个Hash Key的地方就会产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现),这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次.
- 3. 如何理解HashCode的作用
从Object角度看,JVM每个new一个Object,它都会将这个Object丢到一个Hash表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的HashCode再从Hash表中取这个对象,遮掩的目的是提高取对象的效率,若HashCode相同,再去调用equals
String, StringBuffer与StringBuilder的区别
String:适用于少量字符串操作的情况,
StringBuilder适用于单线程下字符缓冲区进行大量操作的情况,线程安全
StringBuilder:线程是不安全的,
这三个类之间的的区别主要是在两个方面,即运行速度和线程安全这两个方面,首先说运行速度,或者是执行速度,在这方面运行速度快慢为
StringBuilder > StringBuffer > String
String最慢的原因
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建以后了对象是不可更改的,但后两者的对象是变量,就是可以更改的
线程安全
在线程安全上StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的.但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作,所以如果要记性的操作是多线程的,那么久要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder.
Map,Set,List,Queue,Stack的特点与用法
Map
① Map是键值对,键Key是唯一不重复的,一个键对应一个值,值可以重复
② TreeMap可以保证顺序
③ HashMap不保证顺序,即为无序的
④ Map中可以将Key和Value单独抽取出来,其中KeySet()方法可以将所有的Key抽取到一个Set,而Values()方法可以将Map中所有Values抽取成一个集合
Set
① 不包含重复集合,set中最多包含一个Null元素
② 只能用Letrator实现单项遍历,Set中没有同步方法.
List
① 有序的重复集合
② 可以在任意位置增删元素
③ 用iterator实现单项遍历,也可以用ListIterator实现双向遍历
Queue
① Queue遵循先进先出原则
② 使用时尽量避免add()和remove()方法,而是使用offer()来添加元素,使用Poll()来移出元素,它的优点是可以通过返回值来判断是否成功
③ LinkedList实现了Queue接口
④ Queue通常不允许插入Null元素
Stack
① Stack遵从后进先出原则
② Stack继承自Vector
③ 它通过五个操作对类Vector进行拓展,允许将向量视为堆栈,它提供了通常的Push和POP操作,以及堆栈定点的peek()方法,测试堆栈是否为空的empty方法等
用法
① 如果涉及堆栈,队列等操作,建议使用List
② 对于快速插入和删除元素的,建议使用LinkedList
③ 如果需要快速随机访问元素的,建议使用ArrayList
更为精炼的总结
Collection是对象集合,Collection有两个子接口,List和Set
List可以通过下标(1,2,3..)来取值,值可以重复,Set只能通过游标来取值,并且值是不能重复的
ArrayList , Vector , LinkedList 是List的实现类
ArrayList是线程不安全的,Vector是线程安全的,这两个底层类都是由数组实现的
LinkedList是线程不安全的,底层是由链表实现的
Map是键值对的集合
① HashTable和HashMap是Map的实现类
② HashTable是线程安全的,不能存储Null值
③ HashMap不是线程安全的,可以存储Null’值
Stack类
继承自Vector,实现一个后进先出的栈,提供了几个基本方法,push,pop,peak,empty,search等
Queue类
提供了几个基本方法,offer,poll,peek等,一直实现类有LinkedList,PriorityQueue等
Try catch finally try里有 return ,finally还会执行吗?
肯定会执行,finally{}块的代码,只有在try{}块中包含遇到System.exit(),之类的导致Java虚拟机直接退出的语句才会不执行.
当程序执行try{}遇到return时,程序会先执行return语句,但并并不会立即返回一一也就是把return语句要做的一些事情都准备好,也就是在将要返回后,但未返回的时候,程序把执行流程转去执行finally块,当finally块执行完成后就直接返回刚才return语句已经准备好的结果.
Java(OOP)面向对象的三个特征与含义
封装(高内聚低耦合 à 解耦)
封装是指将某事物的属性和行为包装到对象中,这个对象只对外部公布需要公开的属性和行为,而这个公布也是可以有选择性的公布给其他对象,在java中能使用private, protected, public 三种修饰符或不用(即默认defalut)对外部对象访问该对象的属性和行为进行限制
.
Java的继承(重用父类的代码)
继承是子对象可以继承父类的属性和行为,亦即父类拥有的属性和行为,其子对象也就拥有了这些属性和行为
Java中的多态(父类引用指向子类对象)
多态是指父对象中的同一个行为能在其多个子对象中由不同的表现
有两种多态的机制:编译时多态,运行时多态
- 方法的重载;重载是指同一类中有多个同名的方法,但这些方法有着不同的参数,因此在编译时就可以确定到底调用哪个方法,它是一种编译时的多态
- 方法的重写:子类可以覆盖父类的方法,因此同样的方法会在父类与子类中有着不同的表现形式
Override和Overload的含义与区别
重载 Overload方法名相同,参数列表不同(个数,顺序,类型不同)与返回类型无关.
重写Override覆盖.将父类的方法覆盖.重写方法重写:方法名相同,访问修饰符只能大于被重写的方法访问修饰符,方法名,顺序个数类型相同
Override(重写)
- 方法名,参数,返回值相同.
- 子类方法不能缩小父类方法的访问权限
- 子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)
- 存在于父类和子类之间
- 方法被定义为final不能被重写
Overload(重载)
- 参数类型,个数,顺序至少有一个不相同
- 2. 不能重载只是返回值不同的方法名
- 存在于父类和子类,同类中
重载的规则
- 必须有不同的参数列表
- 可以有不同的返回类型,只要参数列表不同就可以了
- 可以有不同的访问修饰符
- 可以抛出不同的异常
重写的方法规则
- 参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载
- 返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载
- 访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
- 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
Interface与abstract类的区别
Interface只能有成员变量,只能是方法的声明
Abstract class可以有成员变量,可以声明普通方法和抽象方法
Interface是接口
所有的方法都是抽象方法,成员变量是默认的public static final类型.接口不能实例化自己
Abstract class 是抽象类,至少包含一个抽象方法类叫做抽象类,抽象类不能被自己实例化,并用abstract关键字来修饰
AOP是什么
AOP面向切面编程,是软件开发中的一个热点,是Spring框架内容,利用AOP可对业务逻辑的各个部分隔离,从而使业务逻辑各个部分耦合性降低,提高程序的可重用型,提高开发效率,主要功能:日志记录,性能统计,安全控制,事物处理,异常处理等.
OOP是什么
OOP是面向对象编程,针对业务处理过程的实体及属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分
AOP与OOP的区别
OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分,而AOP则是针对业务处理过程中的切面进行提取,它所面对的时处理过程的某个步骤或阶段以会的逻辑过程中的各部分之间低耦合的隔离效果,这两种设计思想在目标上有着本质的差异
举例:
对于”雇员”这样一个业务实例进行实体封装,自然是OOP的任务,我们可以建立一个”Employee”类,并将”雇员”相关属性和行为封装其中,而从AOP设计思想对”雇员”进行封装则无从谈起
同样,对于”权限检查”这一动作片段进行划分,则是AOP的目标领域
OOP面向名词领域,AOP面向动词领域
总之AOP可以通过预编译方式和运行期动态代理,实现在不修改源码的情况下.给程序动态添加功能的一项技术.
Java开启线程的方法
继承Thread类和实现Runnable接口,由于Java无法实现多重继承,所以一般通过实现Runnable接口来创建线程.但是无论哪种方法都可以通过start()和run()方法来启动线程,下面就来介绍他们的区别
Start方法
通过该方法启动线程的同时也创建了一个线程,真正实现了多线程,无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码,此时start()的这个线程就处于就绪状态,当得到CPU的时间片后,就会执行其中的run()方法.这个run()方法包含了要执行的这个线程的内容,run()方法运行结束,此线程也就终止了..
Run方法:
通过run方法启动线程其实就是调用一个类中的方法,当做普通方法的方式调用,并没有创建一个线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码执行完毕,才会继续执行下面的代码,这样就没有达到写线程的目的
继承Thread类和实现Runnable接口都可以定义一个线程.区别有哪些呢?
实现Runnable接口所具有的优势
- 避免Java单继承的问题
- 适合多线程处理同一资源
- 代码可以被多线程共享,数据独立,很容易实现资源共享
总结:
- start() 可以启动一个新的线程, run()不能
- start() 不能被重复调用, run()可以
- start() 中的run代码可以不执行玩就继续执行下面的代码,即进行了线程切换,直接调用run方法必须等待其代码全部执行完才能继续执行下面的代码
- start() 实现了多线程, run()没有实现多线程