Java基础面试题
1,java两个对象的比较,Java中equals和==之间的区别
-
如果是基本类型比较,那么只能用==来比较,不能用equals。
-
对于基本类型的包装类型,比如Boolean、Character、Byte、Shot、Integer、Long、Float、Double等的引用变量,==是比较地址的,而equals是比较内容的
-
“==”运算符用于比较两个变量的值是否相等,equals()用于比较两个对象中的内容是否一样
-
“==”操作符用于比较我们引用数据的类型的变量的值是否相等,那么equals()比较两个引用变量所指对象的内容是否相等
2,java重载和重写的区别,那个是运行时多态,那个是编译时多态,那个静态绑定,那个是动态绑定。
-
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式,
-
重写:方法名参数都一样;重载:方法名相同,参数不同
-
在Java中存在两种绑定方式,一种为静态绑定,又称作早期绑定;另一种就是动态绑定,也叫后期绑定
-
静态绑定发生在编译时期,动态绑定发生在运行时;静态绑定使用类信息来完成,而动态绑定则需要使用对象信息来完成
-
重载的方法是用静态绑定完成,而重写的方法则使用动态绑定完成,
-
根据何时确定执行多态方法中的哪一个,多态分为两种情况:编译时多态和运行时多态。如果在编译时能够确定执行多态方法中的哪一个,称为编译时多态,否则称为运行时多态。
-
方法重载都是编译时多态。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。
-
方法重写表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。例如,以下声明p、m引用本类实例,调用toString()方法是编译时多态。
3,hashMap和HashTable的区别
-
继承的父类不同,Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
-
线程安全性不同,Hashtable 中的方法是Synchronize的
-
是否提供contains方法,HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同、 -
key和value是否允许null值, Hashtable中,key和value都不允许出现null值,HashMap中,null可以作为键,这样的键只有一个。
-
hash值不同,哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值
-
内部实现使用的数组初始化和扩容方式不同, HashTable在不指定容量的情况下的默认容量为11,而HashMap为16
5,接口和抽象类
-
接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。
-
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)接口可继承接口,并可多继承接口,但类只能单根继承抽象类可以实现接口,而且可以只实现部分接口
-
抽象类可以继承普通类
6.Java中,char型变量中能不能存储一个中文汉字,为什么?
-
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以,char型变量中当然可以存储汉字啦。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。说明:unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
7,能不能自己写个类,也叫Java.util.String
-
可以,但是即使你写了这个类,也没有用。这个问题涉及到加载器的委托机制,
-
在类加载器中,BootStrap是顶层父类,ExtClassLoader是BootStrap类的子类,ExtClassLoader又是AppClassLoader的父类这里以java.lang.String为例,
-
当我是使用到这个类时,Java虚拟机会将java.lang.String类的字节码加载到内存中。为什么只加载系统通过的java.lang.String类而不加载用户自定义的java.lang.String类呢?
-
因加载某个类时,优先使用父类加载器加载需要使用的类。如果我们自定义了java.lang.String这个类,加载该自定义的String类,
-
该自定义String类使用的加载器是AppClassLoader,根据优先使用父类加载器原理,AppClassLoader加载器的父类为ExtClassLoader,
-
所以这时加载String使用的类加载器是ExtClassLoader,但是类加载器ExtClassLoader在jre/lib/ext目录下没有找到String.class类。
-
然后使用ExtClassLoader父类的加载器BootStrap,父类加载器BootStrap在JRE/lib目录的rt.jar找到了String.class,将其加载到内存中。这就是类加载器的委托机制。所以,用户自定义的java.lang.String不被加载,也就是不会被使用。
8,java中集合的结构,以及常用的集合类,各自的特点是什么?
1、List、Set是存储单列的数据集合,都继承与Collection接口。
2、Map是存储键值对这样的双列数据的集合,是个独立接口。
4、List中存储的数据是有序的,可以是重复的。
5、Set中存储的数据是无序的,且不允许重复。
6、Map中存储的数据是无序的,他的键是不允许重复的,值是可以重复的。
List的接口有三个实现类。
1.1 ArrayList
优点: 底层数据结构是数组,查询快,增删慢。
缺点: 线程不安全(一般不考虑到线程的安全因素,用Arraylist效率比较高)
1.2 LinkedList
优点: 底层数据结构是链表,增删快,查询慢。
缺点: 线程不安全。
1.3 Vector
优点: 底层数据结构是数组,查询快,增删慢。线程安全。
缺点: 效率低。
Set接口有三个实现类
2.1 HashSet
为快速查找而设计的Set,依赖hashCode()和equals()方法保证元素的唯一性。如果没有其他限制,这就是我们的默认选择,因为他对速度进行了优化。
2.2 TreeSet
保证元素处于排序状态,底层为树结构,元素必须实现Comparable接口。
2.3 LinkedHashSet
具有HsahSet的查询速度,内部使用链表维护元素的顺序(插入的次序)在使用迭代器遍历Set时,结果会按元素插入的次序显示。元素也必须定义hashCode()方法。
Map接口有6个实现类。
3.1 HashMap
Map基于散列表的实现(取代了Hashtable)。插入和查询"键值对"的开销是固定的。可以通过构造器设置容量和负载因子,以调整容器性能。
3.2 LinkedHashMap
类似于HashMap,但是迭代遍历他时,取得的"键值对"的顺序是其插入次序,或是最近最少使用的(LRU)的次序。只比HashMap慢一点;而在迭代访问时反而更快,因为他使用链表维护内部次序。
3.3 TreeMap
是SortedMap现阶段的唯一实现。基于红黑树实现。查看"键"或者"键值对"时,他们会被排序(次序由Comparable或Comparator j决定)。TreeMap的特点在于,所得到的结果是经过排序的。
3.4 WeakHashMap
弱键(weak key)映射,允许释放映射所指的对象;这是为解决某类特殊问题而设计的。如果映射之外没有引用指向某个"键",则此"键"可以被垃圾回收器回收。
3.5 ConcurrentHashMap
一种线程安全的Map。
3.6 IdentityHashMap
使用"=="代替"equals()"对键进行比较的散列映射,专门解决特殊问题而设计出的。
9,java是如何解决线程安全问题的,有几种方式?有什么不同?
-
使用同步代码块 : synchronized (锁对象) { 可能会产生线程安全问题的代码 }
-
同步方法:在方法声明上加上synchronized同步方法中的锁对象是 this,静态同步方法: 在方法声明上加上static synchronized,静态同步方法中的锁对象是 类名.class
-
同步锁:Lock接口提供了与synchronized关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。使用Lock一定要在try{}cahch中,在finally中释放
10,synchronized和lock的区别?
-
异常时锁的释放:synchronized修饰的代码在执行异常时,jdk会自动释放线程占有的锁,不需要程序员去控制释放锁,因此不会导致死锁现象发生;但是,当Lock发生异常时,如果程序没有通过unLock()去释放锁,则很可能造成死锁现象,因此Lock一般都是在finally块中释放锁。
-
锁的中断:Lock可以让等待锁的线程响应中断处理,如tryLock(long time, TimeUnit unit) ,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够中断,程序员无法控制
-
用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了
11,写一个线程安全的单例模式?
//饥饿模式 public final class EagerSingleton{ private static EagerSingleton singObj = new EagerSingleton(); private EagerSingleton(){ } public static EagerSingleton getSingleInstance(){ return singObj; } }
//懒汉模式 public final class LazySingleton{ private static LazySingleton singObj = null; private LazySingleton(){ }
public static LazySingleton getSingleInstance(){ if(null == singObj ){
singObj = new LazySingleton(); return singObj; } }
//懒汉模式加Synchronized public final class ThreadSafeSingleton{ private static ThreadSafeSingleton singObj = null; private ThreadSafeSingleton(){ }
public static Synchronized ThreadSafeSingleton getSingleInstance(){ if(null == singObj ){
singObj = new ThreadSafeSingleton(); return singObj; } }
12,sleep()和wait()方法的异同?
-
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的
-
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
-
当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。
13,创建线程的四种方式?
-
继承Thread类创建线程
-
实现Runnable接口创建线程
-
使用Callable和Future创建线程
-
使用线程池例如用Executor框架
14,类的加载机制
加载 连接(检验 准备 解析) 初始化
加载就是把.class加载到内存中 验证文件的格式 为类的静态变量分配内存 把类的符号引用转换成直接引用 初始化 静态变量设置正确的初始值