Java面试以及答案

  • HashMap的原理,内部的数据结构
    HashMap是基于hashing的原理,底层使用哈希表(数组 + 链表)实现。里边最重要的两个方法put、get,使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。
      存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度

  • HashMap中put的存储方法过程
    1.对key的hashCode做hash操作,然后再计算在bucket中的index(1.5 HashMap的哈希函数);
    2.如果没碰撞直接放到bucket里;
    3.如果碰撞了,以链表的形式存在buckets后;
    4.如果节点已经存在就替换old value(保证key的唯一性)
    5.如果bucket满了(超过阈值,阈值=loadfactor*current capacity,load factor默认0.75),就要resize。

  • HashMap中hash函数是怎么实现的,还有那些hash的实现方式
    对key的hashCode做hash操作(高16bit不变,低16bit和高16bit做了一个异或); h & (length-1); //通过位操作得到下标index。
    hash的实现方式:数字分析法、平方取中法、分段叠加法、 除留余数法、 伪随机数法。

  • HashMap 怎样解决冲突?
    HashMap中处理冲突的方法实际就是链地址法,内部数据结构是数组+单 链表

  • 抛开 HashMap,hash 冲突有那些解决办法?
    开放定址法、链地址法、再哈希法

  • 两个键的hashcode相同,你如何获取值对象?
    重点在于理解hashCode()与equals()。
      通过对key的hashCode()进行hashing,并计算下标( n-1 & hash),从而获得buckets的位置。两个键的hashcode相同会产生碰撞,则利用key.equals()方法去链表或树(java1.8)中去查找对应的节点。

  • 针对 HashMap 中某个 Entry 链太长,查找的时间复杂度可能达到 O(n),怎么优化?
    将链表转为红黑树,实现 O(logn) 时间复杂度内查找。JDK1.8 已经实现了

  • 如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
    扩容。这个过程也叫作rehashing,因为它重建内部数据结构,并调用hash方法找到新的bucket位置。
      大致分两步:
      1.扩容:容量扩充为原来的两倍(2 * table.length);
      2.移动:对每个节点重新计算哈希值,重新计算每个元素在数组中的位置,将原来的元素移动到新的哈希表中。
    补充:
    loadFactor:加载因子。默认值DEFAULT_LOAD_FACTOR = 0.75f;
    capacity:容量;
    threshold:阈值=capacityloadFactor。当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍(capacity2);
    size:HashMap的大小,它是HashMap保存的键值对的数量

  • 为什么String, Interger这样的类适合作为键
    String, Interger这样的类作为HashMap的键是再适合不过了,而且String最为常用。
      因为String对象是不可变的,而且已经重写了equals()和hashCode()方法了。
      1.不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。
      注:String的不可变性可以看这篇文章《【java基础】浅析String》。
      2.因为获取对象的时候要用到equals()和hashCode()方法,那么键对象正确的重写这两个方法是非常重要的。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能

  • 数组和ArrayList的区别
    数组([]):最高效;但是其容量固定且无法动态改变;
    ArrayList:容量可动态增长;但牺牲效率;
    建议:
    首先使用数组,无法确定数组大小时才使用ArrayList!
    Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。 Array大小是固定的,ArrayList的大小是动态变化的。 ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
    1.效率:
    数组扩容是对ArrayList效率影响比较大的一个因素。
    每当执行Add、AddRange、Insert、InsertRange等添加元素的方法,都会检查内部数组的容量是否不够了,如果是,它就会以当前容量的两倍来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组,
    在这个临界点的扩容操作,应该来说是比较影响效率的。
    ArrayList是Array的复杂版本
    ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。
    2.类型识别:
    ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。
    注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。
    从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了
    3.ArrayList可以存任何Object,如String,Employee等,但不支持基本数据类型,除非使用wrapper

  • HashMap与HashTable区别
    Hashtable可以看做是线程安全版的HashMap,两者几乎“等价”(当然还是有很多不同)。Hashtable几乎在每个方法上都加上synchronized(同步锁),实现线程安全。
    其区别可分为:
    1.HashMap继承于AbstractMap,而Hashtable继承于Dictionary;
      2.线程安全不同。Hashtable的几乎所有函数都是同步的,即它是线程安全的,支持多线程。而HashMap的函数则是非同步的,它不是线程安全的。若要在多线程中使用HashMap,需要我们额外的进行同步处理;
      3.null值。HashMap的key、value都可以为null。Hashtable的key、value都不可以为null;
      4.迭代器(Iterator)。HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException。
      5.容量的初始值和增加方式都不一样:HashMap默认的容量大小是16;增加容量时,每次将容量变为“原始容量x2”。Hashtable默认的容量大小是11;增加容量时,每次将容量变为“原始容量x2 + 1”;
      6.添加key-value时的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法。Hashtable没有自定义哈希算法,而直接采用的key的hashCode()。
      7.速度。由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

  • hashmap 和 concurrenthashmap 的区别
    从JDK1.5开始加入了ConcurrentHashMap,在面试的过程中我问过很多程序员,HashpMap与ConcurrentHashMap的区别是什么呢。我得到的回答一般都是HashpMap不是线程安全的,ConcurrentHashMap是线程安全的。这个结论很笼统。下面我来详细的举例说明他们之间的区别。HashMap 是由于数组+链表组成,数组默认初始容量为 16,Hash 表每次扩容都变为原来的两倍,默认负载因子的大小为0.75(若小于0,抛异常),负载因子是表示一个散列表的空间的使用程度,有这样一个公式:数组容量*负载因子大小=HashMap的容量,如果负载因子是默认的0.75,HashMap(16)的时候,占16个内存空间,实际上只用到了12个,超过12个就扩容(后面介绍,扩容操作及其耗费时间)。如果负载因子是1的话,HashMap(16)的时候,占16个内存空间,实际上会填满16个以后才会扩容
    ConcurrentHashMap
    底层采用分段的数组+链表实现,线程安全
    通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
    Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
    有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
    扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
    详细描述:两者区别
    HashMap与ConcurrentHashMap的区别

  • ConcurrentHashMap原理
    锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
    ConcurrentHashMap提供了与Hashtable和SynchronizedMap不同的锁机制。Hashtable中采用的锁机制是一次锁住整个hash表,从而在同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。
    ConcurrentHashMap默认将hash表分为16个桶,诸如get、put、remove等常用操作只锁住当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。
    在多线程环境下,使用HashMap进行put操作时存在丢失数据的情况,为了避免这种bug的隐患,强烈建议使用ConcurrentHashMap代替HashMap。

HashTable是一个线程安全的类,它使用synchronized来锁住整张Hash表来实现线程安全,即每次锁住整张表让线程独占,相当于所有线程进行读写时都去竞争一把锁,导致效率非常低下。ConcurrentHashMap可以做到读取数据不加锁,并且其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的Hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
CouncurrentHashMap实现原理
ConcurrentHashMap 为了提高本身的并发能力,在内部采用了一个叫做 Segment 的结构,一个 Segment 其实就是一个类 Hash Table 的结构,Segment 内部维护了一个链表数组,我们用下面这一幅图来看下ConcurrentHashMap 的内部结构,从下面的结构我们可以了解到,ConcurrentHashMap 定位一个元素的过程需要进行两次Hash操作,第一次 Hash 定位到 Segment,第二次 Hash 定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是 Hash 的过程要比普通的 HashMap 要长,但是带来的好处是写操作的时候可以只对元素所在的 Segment 进行操作即可,不会影响到其他的 Segment,这样,在最理想的情况下,ConcurrentHashMap 可以最高同时支持 Segment 数量大小的写操作(刚好这些写操作都非常平均地分布在所有的 Segment上),所以,通过这一种结构,ConcurrentHashMap 的并发能力可以大大的提高。
深入ConcurrentHashMap实现原理与使用

  • TreeMap和TreeSet区别和实现原理
    TreeMap采用一种被称为“红黑树”的排序二叉树来保存Map中的每个Entry 每个Entry都被当做红黑树的一个节点来对待;
    TreeMap的插入就是一个“排序二叉树”算法:每当程序添加新节点时,总是从树的根节点开始比较,即将根节点当成当前节点,如果新增节点大于当前节点且当前节点的右节点存在,则以右节点作为当前节点;如果新增节点小于当前节点且当前节点的左节点存在,则以左节点作为当前节点;如果新增节点等于当前节点,则新增节点覆盖当前节点;直到某个节点的左右节点不存在,并结束循环;将新增的节点作为该节点的子节点,如果新增的节点大于该节点,则添加成该节点的右节点,如果新增的节点小于该节点,则添加成该节点的左节点;
    TreeSet底层则采用NavigableMap这个接口来保存TreeSet集合,而实际上NavigableMap只是一个接口,实际上TreeSet还是用TreeMap来保存set元素。
    Java中TreeMap和TreeSet的底层实现
  • 如何实现一个线程安全的队列,可以怎么实现
package com.xue.demo.condition例子;
 
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class Demo {
	public static void main(String[] args) {
		//测试
		final BoundedBuffer boundedBuffer = new BoundedBuffer();
		//放
		for (int i = 0; i < 2; i++) {
			Thread thread = new Thread(new Runnable() {
				public void run() {
					while(true){
						try {
							boundedBuffer.put("你"+new Random().nextInt(100));
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			});
			thread.start();
		}
		//拿
		for (int i = 0; i < 3; i++) {
			Thread thread2 = new Thread(new Runnable() {
				public void run() {
					while(true){
						try {
							boundedBuffer.take();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			});
			thread2.start();
		}
	}
 
}
//jdk的队列例子(线程安全队列),ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类
class BoundedBuffer {
	//锁
   final Lock lock = new ReentrantLock();
   //没有满--阻塞条件
   final Condition notFull  = lock.newCondition(); 
   //没有空--阻塞条件
   final Condition notEmpty = lock.newCondition(); 
   //队列
   final Object[] items = new Object[3];
   //放到什么位置,从哪个位置取,当前队列中元素数量
   int putptr, takeptr, count;
 
   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       Thread.sleep(1000);
       System.out.println(Thread.currentThread().getName()+"--放入--"+x+"--到位置--"+putptr);
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }
 
   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       Thread.sleep(1000);
       System.out.println(Thread.currentThread().getName()+"--取出--"+x+"--从位置--"+putptr);
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }
  • 知道 LRU 吗,20分钟基于 HashMap 实现一个 LRU 算法,面试官给个地址,进去写代码,面试官远程看
    基于HashMap和双向链表的实现
public class LRUCache {
    class Node {
        Node pre;
        Node next;
        Integer key;
        Integer val;
        Node(Integer k, Integer v) {
            key = k;
            val = v;
        }
    }
    Map<Integer, Node> map = new HashMap<Integer, Node>();
    // The head (eldest) of the doubly linked list.
    Node head;
    // The tail (youngest) of the doubly linked list.
    Node tail;
    int cap;
    public LRUCache(int capacity) {
        cap = capacity;
        head = new Node(null, null);
        tail = new Node(null, null);
        head.next = tail;
        tail.pre = head;
    }
    public int get(int key) {
        Node n = map.get(key);
        if(n!=null) {
            n.pre.next = n.next;
            n.next.pre = n.pre;
            appendTail(n);
            return n.val;
        }
        return -1;
    }
    public void set(int key, int value) {
        Node n = map.get(key);
        // existed
        if(n!=null) {
            n.val = value;
            map.put(key, n);
            n.pre.next = n.next;
            n.next.pre = n.pre;
            appendTail(n);
            return;
        }
        // else {
        if(map.size() == cap) {
            Node tmp = head.next;
            head.next = head.next.next;
            head.next.pre = head;
            map.remove(tmp.key);
        }
        n = new Node(key, value);
        // youngest node append taill
        appendTail(n);
        map.put(key, n);
    }
    private void appendTail(Node n) {
        n.next = tail;
        n.pre = tail.pre;
        tail.pre.next = n;
        tail.pre = n;
    }
}

基于LinkedHashMap的实现
HashMap+双向链表?这不就是LinkedHashMap吗!

public class LRUCache {
    private int capacity;
    private Map<Integer, Integer> cache;
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new java.util.LinkedHashMap<Integer, Integer> (capacity, 0.75f, true) {
            // 定义put后的移除规则,大于容量就删除eldest
            protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                return size() > capacity;
            }
        };
    }
    public int get(int key) {
        if (cache.containsKey(key)) {
            return cache.get(key);
        } else
            return -1;
    }
    public void set(int key, int value) {
        cache.put(key, value);
    }
}
  • 二叉树的遍历方式,前序、中序、后序和层序
    二叉树的遍历(前序、中序、后序、层序)

  • 常见的排序算法时间复杂度(排序算法实现也要重点掌握)
    常见的排序算法时间复杂度
    常见的算法
    常见排序算法的总结 - 复杂度、实现和稳定性
    常见排序算法Java实现及复杂度总结

  • 红黑树的特点及相比平衡二叉树的优点(先介绍各自特点)?
    红黑树
    每个节点要么是红色,要么是黑色。
    根节点永远是黑色的。
    所有的叶节点都是空节点(即 null),并且是黑色的。
    每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
    从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。
    平衡二叉树
    任何节点的两个儿子子树的高度最大差别为一
    红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。

  • B+树的了解
    多分支结构有效降低了树的高度
    B 树的各种操作能使 B 树保持较低的高度,从而达到有效避免磁盘过于频繁的查找存 取操作,从而有效提高查找效率

  • Trie-Tree 原理及其应用
    1,字典树
    2,特点
    根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
    从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
    每个节点的所有子节点包含的字符互不相同。
    3,核心思想是空间换时间
    4,应用
    字符串检索
    词频统计

算法题

  • 怎么查询一个单向链表的倒数第五个节点

  • 判断链表是否成环

  • 两条相交的单向链表,如何求他们的第一个公共节点

  • 在无序数组中找最大的K个数?

  • 给定n个数,寻找第k小的数,同时给出时间复杂度

  • 找一个数组中的第三大数

  • 找出数组中第一个出现2次的数,

  • 求 1-N 中数字 1 的个数。

  • 判断一个数是不是丑数;

  • 求第 K 个丑数;

  • 10w行数据,每行一个单词,统计出现次数出现最多的前100个。

  • 一个文本文件,给你一个单词,判断单词是否出现。

  • 一进去要求敲代码二叉排序树的插入、删除及查找

  • 某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名。用户最大规模为2 亿;积分为非负整数,且小于 100 万;

  • 判断一棵二叉树是否是 BST。

  • 一副扑克 54 张牌,现在分成 3 份,每份 18 张,问大小王出现在同一份中的概率是多少;

  • 50个白球50个红球,两个盒子,怎么放让人随机在一个盒子里抽到红球概率最高。。。这个就是一个盒子放一个红球,另一个盒子放99个球。

  • logN 查找一个有序数组移动后类似 4 5 6 7 1 2 3里面的一个数

  • 0 ~ n 连续 n + 1 数,现在有一个长度为 n 的数组存放了上面 n + 1 个数的其中 n 个,找出哪一个数没有被放进数组

  • 将M个平均长度为N的有序队列组合成一个有序队列

  • 10亿条短信,找出前一万条重复率高的

  • 对一万条数据排序,你认为最好的方式是什么

  • 假如有100万个玩家,需要对这100W个玩家的积分中前100名的积分,按照顺序显示在网站中,要求是实时更新的。积分可能由做的任务和获得的金钱决定。问如何对着100万个玩家前100名的积分进行实时更新?

Java 基础

  • Java 的优势
  • boolean 占几个字节
  • Java 访问修饰符权限的区别;
  • String 是否可以继承, “+” 怎样实现?
  • **String,StringBuffer,StringBuilder,区别,项目中那里用到StringBuffer 或者 StringBuilder
  • String为啥不可变,在内存中的具体形态?
  • Comparable 接口和 Comparator 接口实现比较Arrays 静态类如何实现排序的?
  • Java 中异常机制。
  • Java 中异常怎么处理,什么时候抛出,什么时候捕获;
  • 说一说对 java io 的理解
  • 知不知道 NIO
  • Java 锁机制
  • 重入锁、对象锁、类锁的关系
  • 哪些方法实现线程安全?
  • Java 中的同步机制,synchronized 关键字,锁(重入锁)机制,其他解决同步的方 volatile 关键字 ThreadLocal 类的实现原理要懂。
  • Synchronized 和 lock 区别
  • 锁的优化策略
  • Java线程阻塞调用 wait 函数和 sleep 区别和联系,还有函数 yield,notify 等的作用。
  • 谈谈的 Java 反射的理解,怎么通过反射访问某各类的私有属性
  • 动态代理的原理
  • 项目中都是用的框架,用过 Servlet 吗? Servlet 是单例吗?多线程下 Servlet 怎么保证数据安全的?Servlet 的生命周期?
  • Thread 状态有哪些
  • Java实现线程的方式;哪种好;为什么好
  • 单例模式的生命周期
  • 继承和多态的区别;
  • Java8 的新特性。

Java 高级面试题

  • GC 算法,除了常见的复制算法,标记整理,标记清除算法,还有哪些?
  • 垃圾收集器
  • Java 内存模型
  • 问 JVM 内存分代机制(会问分为那几个代,各个代特点),分代回收的优点(这个问了很多次)。
  • Java 虚拟机类加载机制,双亲委派模型
  • minor GC 和 Full GC 的触发时机
  • JVM 中什么样的对象是可以回收的,对象从新年代到老年代的转移过程,JVM 哪些地方会溢出(除了程序计数器都有)
  • Java虚拟机的一些参数配置
  • 什么情况会栈溢出
  • JDK1.8 中 JVM 做了那些改变
  • 常用 JVM 调优工具有哪些(Jstatus,JStack,Jmap等),有没有调有经验.
  • 知道 OOM 吗,举一个 OOM 的例子
  • 介绍一下 Java 的强软弱虚四种引用,问什么时候使用软引用
  • RPC 原理;

java框架

  • Spring 主要思想是什么,回答 IOC 和AOP,怎么自己实现 AOP ?
  • SpringAOP 用的哪一种代理
  • spring bean 初始化过程
  • spring bean 对象的生命周期
  • 讲讲 Spring 中 ApplicationContext 初始化过程。
  • SpringMVC 处理请求的流程
  • SpringMVC 的设计模式
  • Spring 的 annotation 如何实现
  • Spring拦截器怎么使用,Controller是单例吗

数据库

  • SQL 优化方案
  • 索引有哪些?分别有什么特点?
  • 索引为什么用 B 树不用二叉树,有什么好处?
  • 数据库索引优点和缺点
  • 数据库事务的四个隔离级别,MySql 在哪一个级别
  • 数据库,两次相同的 select 操作,期间没有发生增,删,改操作,返回的结果是否相同;
  • 怎么设计数据库表(从范式角度,可以加一些设计惯例)
  • MySQL 存储引擎有哪些,INNODB 和 MYISAM 的区别
  • 实践中如何优化 MySQL
  • 慢查询
  • int(8) 和 int(10) 的区别是什么

操作系统

  • 进程和线程的区别
  • 进程间通信
  • 在共享内存中如何使用 mutex
  • select 和 epoll
  • 操作系统由哪几部分组成,进程结构
  • 多进程和多线程的区别
  • 什么时候使用多线程,什么时候使用多进程
  • Java 多线程与操作系统线程的关系
  • 一般线程和守护线程的区别、
  • 多线程与多线程的实现方式
  • 死锁的条件,死锁避免。
  • linux中如何查看进程等命令
  • 不同进程打开了同一个文件,那么这两个进程得到的文件描述符(fd)相同吗
  • 两个线程如何同时监听一个端口。

计算机网络

  • HTTP 状态码有哪些,一一解释含义
  • HTTP 请求头有哪些,介绍平时见过的,怎么利用这些信息来进行前后端调试
  • TCP 和 UDP 的区别
  • TCP 如何保证可靠性
  • 拥塞控制与流量控制的区别
  • OSI七层模型,每层对应的协议有哪些,每层有何含义
  • 网络浏览器访问一个网址发生了什么过程
  • Cookie 和 Session 的区别
  • HTTP1.0 和 1.1 的区别
  • HTTP 和 HTTPS 的主要区别
  • 滑动窗口算法
  • 域名解析详细过程
  • IP 地址分为几类,每类都代表什么,私网是哪些
  • IP 头组成;
  • 计算机网络中的同步和异步
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值