目录
你是怎样理解面向对象的
面向对象是利用语言对现实事物抽象。面向对象具有以下四大特征:
- 封装:把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的方法
- 继承:从已有的类得到继承信息创造新类
- 多态:允许不同子类型的对象对同一消息作出不同的响应
- 抽象:将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面
int和Integer有什么区别
- int是基本数据类型,Integer是int的包装类
- Integer必须实例化才能使用,int不需要
- Integet的默认值是null,int是0
- Integer是对象的引用,当new Integer时,会生成一个指针指向此对象,而int直接存储数据值
==和equals的区别
- ==
- 如果比较的是基本数据类型,那么比较的是变量的值
- 如果比较的是引用数据类型,那么比较的是地址值(两个对象是否指向同一块内存)
- equals
- 如果没重写 equals 方法比较的是两个对象的地址值
- 如果重写了 equals 方法后我们往往比较的是对象中的属性的内容
- equals 方法是从 Object 类中继承的,默认的实现就是使用==
谈谈你对反射的理解
反射是通过实例或者类名获得类信息,一般反射和泛型可以同使用,典型的Spring整合其他框架时候,通过FactoryBean接口和泛型反射就可以为Spring容器注入需要的bean
反射机制
所谓的反射机制就是Java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的 动作做准备。
Java 的反射机制的实现要借助于 4 个类:Class,Constructor,Field,Method。
- Class:类的类对象
- Constructor:类的构造器对象
- Field:类的属性对象
- Method:类的方法对象
反射的作用
在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。动态获取类的信息以及动态调用对象的方法。
ArrarList和LinkedList区别
- ArrayList底层是基于动态数组的数据结构,LinkedList底层基于链表的数据结构
- 对于get和set,ArrayList优于LinkedList,因为LinkedList要移动指针
- 对于add和remove,LinkedList优于ArrayList,因为ArrayList要移动数据
HashMap底层
HashMap的底层结构在jdk1.7中由数组+链表实现,在jdk1.8中由数组+链表+红黑树实现。
HashMap底层结构图:
HashMap.put()执行流程图:
HashMap和HashTable的区别
- 线程安全性不同
- HashMap是线程不安全的,HashTable是线程安全的,其中的方法是Synchronize的,在多线程并发的情况下,可以直接使用HashTable,但使用HashMap时必须自己增加同步处理
- 是否提供contains()
- HashMap只有containsValue()和containsKey();HashTable有contains()、containsKey()和containsValue(),其中contains()和containsValue()方法功能相同
- key和value是否允许null值
- HashTable中,key和value都不允许出现null值;HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null
- 数组初始化和扩容机制
- HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,HashTable不要求底层数组的容量一定要为2的整次幂,而HashMap要求一定为2的整次幂。HashTable扩容时,将容量变为原来的2倍+1,而HashMap扩容时,将容量变为原来的2倍
TreeSet和HashSet的区别
HashSet是采用Hash表来实现的,其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。
TreeSet是采用树结构实现(红黑树算法),元素是按顺序进行排列,add()、remove()以及 contains()等方法都是复杂度为O(log(n))的方法,它还提供了一些方法来处理排序的set,如first()、last()、headSet()、tailSet()等等。
StringBuffer和StringBuild的区别
- StringBuffer与StringBuilder中的方法和功能完全是等价的
- 只是StringBuffer中的方法大都采用了synchronized关键字进行修饰,因此是线程安全的,而 StringBuilder没有这个修饰,可以被认为是线程不安全的
- 在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低
final、finally、finalize的区别
final:修饰符(关键字)有三种用法(修饰类、变量和方法)。修饰类时,意味着它不能再派生出新的子类,即不能 被继承,因此它和 abstract 是反义词。修饰变量时,该变量使用中不被改变,必须在声明时给定初值,在引用中只 能读取不可修改,即为常量。修饰方法时,也同样只能使用,不能在子类中被重写。
finally:通常放在try…catch的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。
线程有几种状态,产生的条件是什么
- 新建状态(New)
- 线程对象被创建后,就进入了新建状态。例如Thread thread = new Thread()
- 就绪状态(Runnable)
- 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如thread.start()。处于就绪状态的线程,随时可能被CPU调度执行
- 运行状态(Running)
- 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态
- 阻塞状态(Blocked)
- 阻塞状态是线程因为某种原因放弃 CPU 使用权,暂时停止运行。直到线程进入就绪状态, 才有机会转到运行状态。阻塞的情况分三种(①等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成;②同步阻塞:线程在获取synchronized同步锁失败;③其他阻塞:通过调用线程的sleep()或join()或发出了IO请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或超时、或者 IO 处理完毕时,线程重新转入就绪状态。)
- 死亡状态(Dead)
- 线程执行完了或者因异常退出了run()方法,该线程结束生命周期
产生死锁的原因
- 系统资源不足
- 进程运行推进的顺序不合适
- 资源分配不当
- ......
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
什么是线程池,如何使用
线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。
在JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
然后调用他们的execute()方法即可。
使用线程池有何优点
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行
- 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳 定性,使用线程池可以进行统一的分配,调优和监控
Java自带有哪几种线程池
- Executors.newCachedThreadPool()
- 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种类型的线程池特点是:工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE),这样可灵活的往线程池中添加线程。如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪
- Executors.newFixedThreadPool(4)
- 创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始 的最大数,则将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源
- Executors.newSingleThreadExecutor()
- 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO、LIFO、优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。 单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的
- Executors.newScheduledThreadPool(4)
- 创建一个定长的线程池,而且支持定时的以及周期性的任务执行。例如延迟 3 秒执行
Java中有几种类型的流
字节流如何转为字符流
字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。
字节输出流转字符输出流通过OutputStreamWriter实现,该类的构造函数可以传入OutputStream对象。
请说出你最常见的RuntimeException
- java.lang.NullPointerException(空指针异常)
- 出现原因:调用了未经初始化的对象或者是不存在的对象
- java.lang.ClassNotFoundException(指定的类找不到)
- 出现原因:类的名称和路径加载错误,通常都是程序试图通过字符串来加载某个类时可能引发异常
- java.lang.NumberFormatException(字符串转换为数字异常)
- 出现原因:字符型数据中包含非数字型字符
- java.lang.IndexOutOfBoundsException(数组角标越界异常)
- 出现原因:常见于操作数组对象时发生
- java.lang.IllegalArgumentException(方法传递参数错误)
- 出现原因:调用方法时传参错误
- java.lang.ClassCastException(数据类型转换异常)
- 出现原因:常见于进行数据类型转换时发生