java 基础面试题

java 堆与栈

1、Java的堆是一个运行时数据区,类的对象从堆中分配空间。这些对象通过new等指令建立,通过垃圾回收器来销毁。
2、堆的优势是可以动态地分配内存空间,需要多少内存空间不必事先告诉编译器,因为它是在运行时动态分配的。但缺点是,由于需要在运行时动态分配内存,所以存取速度较慢。

1、 栈中主要存放一些基本数据类型的变量(byte,short,int,long,float,double,boolean,char)和对象的引用。
2、栈的优势是,存取速度比堆快,栈数据可以共享。但缺点是,存放在栈中的数据占用多少内存空间需要在编译时确定下来,缺乏灵活性。

String str1 = new String("abc");
String str2 = "abc";
第一种使用new来创建的对象,它存放在堆中。每调用一次就创建一个新的对象。
第二种是先在栈中创建对象的引用str2,然后查找栈中有没有存放“abc”,如果没有,则将“abc”存放进栈,并将str2指向“abc”,如果已经有“abc”, 则直接将str2指向“abc”。

Java 数组与链表

数组是一种连续存储线性结构,元素类型相同,大小相等,数组是多维的,通过使用整型索引值来访问他们的元素,数组尺寸不能改变。


image.png

数组的优点:存取速度快
数组的缺点:事先必须知道数组的长度、插入删除元素很慢、
空间通常是有限制的、需要大块连续的内存块、插入删除元素的效率很低

2、链表是有data和指向下一个数据的指针地址两部分组成


image.png

链表的优点: 插入删除速度快、 内存利用率高,不会浪费内存
、大小没有固定,拓展很灵活。
链表的缺点:不能随机查找,必须从第一个开始遍历,查找效率低。

HashMap的原理

HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,

image.png

哈希表结构:结合数组结构和链表结构的优点,从而实现了查询和修改效率高,插入和删除效率也高的一种数据结构


image.png

HashMap 为何默认容量为16?
HashMap的默认长度为16,是为了降低hash碰撞的几率。

/* 
 将hashcode换成链表数组中的下标
 X % 2^n = X & (2^n – 1)
位运算(&)效率要比代替取模运算(%)高很多
**/
static int indexFor(int h, int length) {
    return h & (length-1);
}

HashMap和Hashtable区别?
HashMap:
(1)由数组+链表组成的,基于哈希表的Map实现,数组是HashMap的主体,
链表则是主要为了解决哈希冲突而存在的。
(2)不是线程安全的,HashMap可以接受为null的键(key)和值(value)。
(3)HashMap重新计算hash值
Hashtable:
(1)Hashtable 是一个散列表,它存储的内容是键值对(key-value)映射。
(2)Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。
(3)HashTable直接使用对象的hashCode。

双重检查锁,为什么要加volatile

1、保证可见性。使用 volatile 定义的变量,将会保证对所有线程的可见性。
2、禁止指令重排序优化。

public class Singleton { 
 
  private volatile static Singleton instance;
 
  private Singleton() {}
 
  public static Singleton getInstance() {
    if (instance == null) {
      synchronized(Singleton.class) { // 此处为类级别的锁
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }

CAS

  • Compare and Swap 比较并交换
  • 内存地址的值V,旧的预期的值A,要修改的新值B。只要当V与A相等时,才会将V修改为新的值B。
  • Unsafe将cas编译成一条cpu指令,没有函数调用
  • aba问题:当前值可能是变为b后再变为a,此a非彼a,通过加版本号能解决
  • 非阻塞同步:没有挂起唤醒操作,多个线程同时修改一个共享变量时,只有一个线程会成功,其余失败,它们可以选择轮询。

ReentrantLock和synchronized的区别

  • ReentrantLock 需要显示的释放锁,lock.unlock()。
  • ReentrantLock 构造器传入true实现公平锁。
  • ReentrantLock 等待可中断,当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
  • ReentrantLock 可以绑定多个条件,多次调用newCondition()同时绑定多个Condition对象。

进程与线程的区别

1、进程是表示资源分配的基本单位,又是调度运行的基本单位。
2、线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。

线程的优点:
(1)易于调度。
(2)提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。
(3)开销少。创建线程比创建进程要快,所需开销很少。。
(4)利于充分发挥多处理器的功能。通过创建多线程进程(即一个进程可具有两个或更多个线程),每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分运行。

进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)处理机分给线程,即真正在处理机上运行的是线程。
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

内存回收机制与GC算法

内存判定对象可回收有两种机制:

1、引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法。

2、可达性分析法:通过一系列被称为『GCRoots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

3 、可作为GC Roots的对象:

  • 虚拟机栈中引用的对象,主要是指栈帧中的本地变量
  • 本地方法栈中Native方法引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
GC回收算法有以下四种:

分代收集算法:是当前商业虚拟机都采用的一种算法,根据对象存活周期的不同,将Java堆划分为新生代和老年代,并根据各个年代的特点采用最适当的收集算法。

新生代:大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可。
复制算法: 把可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象『复制』到另外一块上面,再将这一块内存空间一次清理掉。实现简单,运行高效。在对象存活率较高时就要进行较多的复制操作,效率将会变低。

老年代:对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。
标记-清除算法: 首先『标记』出所有需要回收的对象,然后统一『清除』所有被标记的对象。标记和清除两个过程的效率都不高,清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-整理算法: 首先『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。标记整理算法会将所有的存活对象移动到一端,并对不存活对象进行处理,因此其不会产生内存碎片。

Http和Https区别

HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

java线程池

线程池:降低资源消耗,提高响应速度,提高线程的可管理性。

  • 1.缓存线程池 (newCachedThreadPool)
    /**
         *缓存线程池.
         * (长度无限制)
         * 执行流程:
         *      1. 判断线程池是否存在空闲线程
         *      2. 存在则使用
         *      3. 不存在,则创建线程 并放入线程池, 然后使用 
         * */
        ExecutorService service = Executors.newCachedThreadPool();
        //向线程池中 加入 新的任务
        service.execute(new Runnable() {
            @Override public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });

        service.execute(new Runnable() {
            @Override public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });

        service.execute(new Runnable() {
            @Override public void run() {
                System.out.println("线程的名称:"+Thread.currentThread().getName());
            }
        });
  • 2.定长线程池 (newFixedThreadPool)
 /** 
         * 定长线程池.(长度是指定的数值) 
         * 执行流程:
         *      1. 判断线程池是否存在空闲线程 
         *      2. 存在则使用 
         *      3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
         *      4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 
         * */
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
  • 3.单线程线程池 (newSingleThreadExecutor)
/**
         *  单线程线程池.
         *  执行流程:
         *      1. 判断线程池 的那个线程 是否空闲
         *      2. 空闲则使用 
         *      3. 不空闲,则等待 池中的单个线程空闲后 使用
         * */
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
  • 4.周期性任务定长线程池 (newScheduledThreadPool)
/**
         *  周期任务 定长线程池.
         *  执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
         * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
         *
         * 周期性任务执行时:
         * 定时执行, 当某个时机触发时, 自动执行某任务 .
         * */
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

        /**
         *  定时执行
         *  参数1. runnable类型的任务
         *  参数2. 时长数字
         *  参数3. 时长数字的单位
         *  */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(" hello world ");
            }
        }, 5, TimeUnit.SECONDS);

        /**
         *  周期执行 
         *  参数1. runnable类型的任务 
         *  参数2. 时长数字(延迟执行的时长) 
         *  参数3. 周期时长(每次执行的间隔时间) 
         *  参数4. 时长数字的单位 */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(" hello world ");
            }
        }, 5, 2, TimeUnit.SECONDS);

你可能感兴趣的:(java 基础面试题)