1.简单讲一下java的跨平台原理。
由于各操作系统支持的指令集不是完全统一的,因此会让程序在不同操作系统需要不同的程序。Java开发了适用于不同版本系统的JVM虚拟机来屏蔽各系统之间的差异,提供统一的接口。
2.讲下java的数据类型。
基本数据类型和引用类型的区别主要在于基本数据类型是分配在栈上的,而引用类型是分配在堆上的。
Java中的数据类型分为引用数据类型和基本数据类型。引用数据类型分3种:类,接口,数组;
基本数据类型又分布尔类型和数值类型;
- byte:Java中最小的数据类型,在内存中占8位(bit),即1个字节,取值范围-128~127,默认值0
- short:短整型,在内存中占16位,即2个字节,取值范围-32768~32767,默认值0
- int:整型,用于存储整数,在内在中占32位,即4个字节,取值范围-2147483648~2147483647,默认值0
- long:长整型,在内存中占64位,即8个字节-263~263-1,默认值0L
- float:浮点型,在内存中占32位,即4个字节,用于存储带小数点的数字(与double的区别在于float类型有效小数点只有6~7位),默认值0
- double:双精度浮点型,用于存储带有小数点的数字,在内存中占64位,即8个字节,默认值0
- char:字符型,用于存储单个字符,占16位,即2个字节,取值范围0~65535,默认值为空
- boolean:布尔类型,占1个字节,用于判断真或假(仅有两个值,即true、false),默认值false
3.面向对象有哪些特征,简要说明。
- 封装:封装就是把过程和数据包围起来,对数据的访问只能通过特定的界面.如私有变量,用set,get方法获取
- 继承:继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法
- 抽象:忽略一个主题中与当前目标无关的东西,专注的注意与当前目标有关的方面.(就是把现实世界中的某一类东西,提取出来,用程序代码表示,抽象出来的一般叫做类或者接口).抽象并不打算了解全部问题,而是选择其中的一部分,暂时不用部分细节.抽象包括两个方面,一个数据抽象,而是过程抽象.
数据抽象 -->表示世界中一类事物的特征,就是对象的属性.比如鸟有翅膀,羽毛等(类的属性)
过程抽象 -->表示世界中一类事物的行为,就是对象的行为.比如鸟会飞,会叫(类的方法) - 多态性:多态是指允许不同类的对象对同一消息做出响应。多态性语言具有灵活/抽象/行为共享/代码共享的优势,很好的解决了应用程序函数同名问题.总的来说,方法的重写,重载与动态链接构成多态性.java引入多态的概念原因之一就是弥补类的单继承带来的功能不足.
动态链接 -->对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将调用子类中的这个方法,这就是动态链接.
4.有了基本数据类型,为什么还要包装类型?
Java是面向对象的语言,而基本数据类型不具有面向对象的 特性。Max,Min,Null。
5.说下"=="和equals方法究竟有什么区别?
- 等号操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。
- equals 方法是用于比较两个独立对象的内容是否相同。
6.讲下String,StringBuilder和StringBuffer的区别。
- 速度运行效率。String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
- 线程安全。StringBuilder是线程不安全的,而StringBuffer是线程安全的
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
7.讲下java的集合。
集合分为:List,Set,Map三种,其中List与Set是继承自Collection,而Map不是。
List与Set的区别:List中的元素有存放顺序,并且可以存放重复元素,检索效率高,插入删除效率低,Set没有存放顺序,而且不可以存放重复元素,后来的元素会把前面重复的元素替换掉,检索效率低,插入删除效率高。(Set存储位置是由它的HashCode码决定的,所以它存储的对象必须有equals()方法,而且Set遍历只能用迭代,因为它没有下标。)
- 最常用的集合:ArrayList
特点:ArrayList集合中元素存储的位置是连续的,所以查询起来会比较快捷,但是执行插入删除操作会比较麻烦一点,会引起其他元素位置的变化。 - 与最常用集合相反的集合:LinkedList
LinkedList与ArrayList是互补的,所以ArrayList的优点就是LinkedList的缺点,ArrayList的缺点就是LinkedList的优点。
特点:LinkedList中元素位置是任意的,所以执行插入删除操作效率较高,查询效率较低。 - 与一般集合都相反的集合:Vector
特点:多个线程同时访问不会发生不确定的结果,但是它的效率会比较低,如果要考虑线程安全的话可以用它。 - Set中最常用的集合:HashSet
HashSet是使用Hash表实现的,集合里面的元素是无序得,可以有null值,但是不能有重复元素。
特点:因为相同的元素具有相同的hashCode,所以不能有重复元素 - Set中第二常用的集合:TreeSet
TreeSet是用二叉树结构实现的集合
特点:集合中的元素是有顺序得,不允许放入null,同样不能放入重复元素。 - 第二常用的集合:HashMap
使用键值对存储的场景,而HashMap是用得最多的一种键值对存储的集合。
特点:HashMap允许空键值,并且它是非线程安全的,所以插入、删除和定位元素会比较快。 - 一些不太常用的Map集合:TreeMap,HashTable
TreeMap是基于红黑树实现的,适用于按自然顺序遍历key。
HashTable是基于HashCode实现的,但它是线程安全的,所以会比HashMap效率低,而且不允许null值。
8.讲下HashMap,HashTable,CurrentTable的区别。
HashMap是线程非安全的,效率比较高;HashTable和CurrentHashMap是线程安全的,效率比HashMap差一点,但CurrentHashMap比HashTable更加高效一些,因为CurrentHashMap采用了更加高效的分段锁机制。
9.讲下线程的几种实现方式,怎么启动,怎么区分?
①实现方式
1、通过继承 Thread类实现一个线程
2、通过实现 Runnable接口实现一个线程
继承扩展性不强,java总只支持单继承,如果一个类继承 Thread就不能继承其他的类了。
②怎么启动?
Thread thread = new Thread(继承了 Thread的对象/实现了 Runnable的对象)
thread.setName(“设置一个线程名称”);
thread.start();
启动线程使用 start方法,而启动了以后执行的是 run方法。
③怎么区分线程?在一个系统中有很多线程,每个线程都会打印日志,我想区分是哪个线程
打印的怎么办?
thread.setName(“设置一个线程名称
10.有没有使用过java并发线程库?
简单了解过。通常开发者都是利用Executors提供的通用线程池创建方法,去创建不同配置的线程池,主要区别在于不同的ExecutorService类型或者不同的初始参数。Executors目前提供了5种不同的线程池创建配置:
- newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池。具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过60秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SychronousQueue作为工作队列。
- newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目nThreads。
- newSingleThreadExecutor(),它的特点在于工作线程数目被限制为1,操作一个无界的工作队列,所以它保证了所有任务都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
- newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledThreadPoolExecutor,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
- newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。
11.线程池的作用。
- 限定线程的个数,不会导致由于线程过多而导致系统运行缓慢或崩溃。
- 线程池不需要每次都去创建或消耗,节约了资源。
- 线程池不需要每次都创建,响应时间快。
11、讲下什么是设计模式,常用的设计模式是那些,写出单例模式的实现。
- 设计模式是前人经过无数次的实践经验总结出来的,设计过程可以反复使用的,可以解决特定问题的设计方法。
- 工厂模式,单例模式,适配器模式,代理模式,命令模式,策略模式。
懒汉模式(线程不安全):
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式,申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且在多线程不能正常工作。
懒汉模式(线程安全):
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。
双重检查模式 (DCL):
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){
}
public static Singleton getInstance() {
if (instance== null) {
synchronized (Singleton.class) {
if (instance== null) {
instance= new Singleton();
}
}
}
return singleton;
}
}
检查模式是正确使用volatile关键字的场景之一。
在这里使用volatile会或多或少的影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。 DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL。
静态内部类单例模式:
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化sInstance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。
使用容器实现单例模式:
public class SingletonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private Singleton() {
}
public static void registerService(String key, Objectinstance) {
if (!objMap.containsKey(key) ) {
objMap.put(key, instance) ;
}
}
public static ObjectgetService(String key) {
return objMap.get(key) ;
}
}
用SingletonManager 将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
饿汉模式:这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。 这种方式基于类加载机制避免了多线程的同步问题,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果
public class Singleton {
// 类共享实例对象 实例化
private static Singleton singleton = new Singleton();
// 私有构造方法
private Singleton() {
System.out.println("-- this is Singleton!!!");
}
// 获得单例方法
public static Singleton getInstance() {
// 直接返回共享对象
return singleton;
}
}