Mybatis源码解析:21道Java基础面试题及答案

2023-05-16

10. HashMap和HashTable的区别。

答案:Hashtable和HashMap类有三个重要的不同之处。

1、第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。

2、也许最重要的不同是Hashtable的方法是同步的,但是这也是HashTable效率低下的原因,任何方法都是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。

3、第三点不同是,**只有HashMap可以让你将空值作为一个表的条目的key或value。**HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。

一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。

11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。

(1)HashMap的源码:

1、HashMap中的hash函数实现:

详见:https://www.zhihu.com/question/20733617

2、HashMap源码解读

详见:http://blog.csdn.net/ll530304349/article/details/53056346

(2)ConcurrentHashMap的源码:

1、JDK1.7版本的实现

ConcurrentHashMap的锁分段技术:假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

ConcurrentHashMap不允许Key或者Value的值为NULL

第一:Segment类

Put

将一个HashEntry放入到该Segment中,使用自旋机制,减少了加锁的可能性。


final V put(K key, int hash, V value, boolean onlyIfAbsent) {

   HashEntry<K,V> node = tryLock() ? null :

       scanAndLockForPut(key, hash, value); //如果加锁失败,则调用该方法

   V oldValue;

   try {

       HashEntry<K,V>[] tab = table;

       int index = (tab.length - 1) & hash; //同hashMap相同的哈希定位方式

       HashEntry<K,V> first = entryAt(tab, index);

       for (HashEntry<K,V> e = first;;) {

           if (e != null) {

       //若不为null,则持续查找,知道找到key和hash值相同的节点,将其value更新

               K k;

               if ((k = e.key) == key ||

                   (e.hash == hash && key.equals(k))) {

                   oldValue = e.value;

                   if (!onlyIfAbsent) {

                       e.value = value;

                       ++modCount;

                   }

                   break;

               }

               e = e.next;

           }

           else { //若头结点为null

               if (node != null) //在遍历key对应节点链时没有找到相应的节点

                   node.setNext(first);

                   //当前修改并不需要让其他线程知道,在锁退出时修改自然会

                   //更新到内存中,可提升性能

               else

                   node = new HashEntry<K,V>(hash, key, value, first);

               int c = count + 1;

               if (c > threshold && tab.length < MAXIMUM_CAPACITY)

                   rehash(node); //如果超过阈值,则进行rehash操作

               else

                   setEntryAt(tab, index, node);

               ++modCount;

               count = c;

               oldValue = null;

               break;

           }

       }

   } finally {

       unlock();

   }

   return oldValue;

} 

scanAndLockForPut

该操作持续查找key对应的节点链中是否已存在该节点,如果没有找到已存在的节点,则预创建一个新节点,并且尝试n次,直到尝试次数超出限制,才真正进入等待状态,即所谓的 自旋等待


private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {

   //根据hash值找到segment中的HashEntry节点

   HashEntry<K,V> first = entryForHash(this, hash); //首先获取头结点

   HashEntry<K,V> e = first;

   HashEntry<K,V> node = null;

   int retries = -1; // negative while locating node

   while (!tryLock()) {  //持续遍历该哈希链

       HashEntry<K,V> f; // to recheck first below

       if (retries < 0) {

           if (e == null) {

               if (node == null) //若不存在要插入的节点,则创建一个新的节点

                   node = new HashEntry<K,V>(hash, key, value, null);

               retries = 0;

           }

           else if (key.equals(e.key))

               retries = 0;

           else

               e = e.next;

       }

       else if (++retries > MAX_SCAN_RETRIES) {

       //尝试次数超出限制,则进行自旋等待

           lock();

           break;

       }

       /*当在自旋过程中发现节点链的链头发生了变化,则更新节点链的链头,

       并重置retries值为-1,重新为尝试获取锁而自旋遍历*/

       else if ((retries & 1) == 0 &&

                (f = entryForHash(this, hash)) != first) {

           e = first = f; // re-traverse if entry changed

           retries = -1;

       }

   }

   return node;

} 

remove

用于移除某个节点,返回移除的节点值。


final V remove(Object key, int hash, Object value) {

   if (!tryLock())

       scanAndLock(key, hash);

   V oldValue = null;

   try {

       HashEntry<K,V>[] tab = table;

       int index = (tab.length - 1) & hash;

       //根据这种哈希定位方式来定位对应的HashEntry

       HashEntry<K,V> e = entryAt(tab, index);

       HashEntry<K,V> pred = null;

       while (e != null) {

           K k;

           HashEntry<K,V> next = e.next;

           if ((k = e.key) == key ||

               (e.hash == hash && key.equals(k))) {

               V v = e.value;

               if (value == null || value == v || value.equals(v)) {

                   if (pred == null)

                       setEntryAt(tab, index, next);

                   else

                       pred.setNext(next);

                   ++modCount;

                   --count;

                   oldValue = v;

               }

               break;

           }

           pred = e;

           e = next;

       }

   } finally {

       unlock();

   }

   return oldValue;

} 

Clear

要首先对整个segment加锁,然后将每一个HashEntry都设置为null。


final void clear() {

   lock();

   try {

       HashEntry<K,V>[] tab = table;

       for (int i = 0; i < tab.length ; i++)

           setEntryAt(tab, i, null);

       ++modCount;

       count = 0;

   } finally {

       unlock();

   }

} 

Put


public V put(K key, V value) {

   Segment<K,V> s;

   if (value == null)

       throw new NullPointerException();

   int hash = hash(key); //求出key的hash值

   int j = (hash >>> segmentShift) & segmentMask;

   //求出key在segments数组中的哪一个segment中

   if ((s = (Segment<K,V>)UNSAFE.getObject           

        (segments, (j << SSHIFT) + SBASE)) == null)  

       s = ensureSegment(j); //使用unsafe操作取出该segment

   return s.put(key, hash, value, false); //向segment中put元素

} 

Get


public V get(Object key) {

   Segment<K,V> s;

   HashEntry<K,V>[] tab;

   int h = hash(key); //找出对应的segment的位置

   long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;

   if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&

       (tab = s.table) != null) {  //使用Unsafe获取对应的Segmen

       for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile

                (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);

            e != null; e = e.next) { //找出对应的HashEntry,从头开始遍历

           K k;

           if ((k = e.key) == key || (e.hash == h && key.equals(k)))

               return e.value;

       }

   }

   return null;

} 

Size

求出所有的HashEntry的数目,先尝试的遍历查找、计算2遍,如果两遍遍历过程中整个Map没有发生修改(即两次所有Segment实例中modCount值的和一致),则可以认为整个查找、计算过程中Map没有发生改变。否则,需要对所有segment实例进行加锁、计算、解锁,然后返回。


public int size() {



  final Segment<K,V>[] segments = this.segments;

  int size;

  boolean overflow; // true if size overflows 32 bits

  long sum;         // sum of modCounts

  long last = 0L;   // previous sum

  int retries = -1; // first iteration isn't retry

  try {

      for (;;) {

          if (retries++ == RETRIES_BEFORE_LOCK) {

              for (int j = 0; j < segments.length; ++j)

                  ensureSegment(j).lock(); // force creation

          }

          sum = 0L;

          size = 0;

          overflow = false;

          for (int j = 0; j < segments.length; ++j) {

              Segment<K,V> seg = segmentAt(segments, j);

              if (seg != null) {

                  sum += seg.modCount;

                  int c = seg.count;

                  if (c < 0 || (size += c) < 0)

                      overflow = true;

              }

          }

          if (sum == last)

              break;

          last = sum;

      }

  } finally {

      if (retries > RETRIES_BEFORE_LOCK) {

          for (int j = 0; j < segments.length; ++j)

              segmentAt(segments, j).unlock();

      }

  }

  return overflow ? Integer.MAX_VALUE : size;

} 

(2)JDK1.8实现

在JDK1.8中对ConcurrentHashmap做了两个改进:

  • 取消segments字段,直接采用transient volatile HashEntry<K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率

  • 将原先 table数组+单向链表 的数据结构,变更为 table数组+单向链表+红黑树 的结构。对于hash表来说,最核心的能力在于将key hash之后能均匀的分布在数组中。如果hash之后散列的很均匀,那么table数组中的每个队列长度主要为0或者1。但实际情况并非总是如此理想,虽然ConcurrentHashMap类默认的加载因子为0.75,但是在数据量过大或者运气不佳的情况下,还是会存在一些队列长度过长的情况,如果还是采用单向列表方式,**那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),**可以改进性能。

12. TreeMap、HashMap、LinkedHashMap的区别。

Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值

重复。Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

  • HashMap:线程不同步。根据key的hashcode进行存储,内部使用静态内部类Node的数组进行存储,默认初始大小为16,每次扩大一倍。当发生Hash冲突时,采用拉链法(链表)。可以接受为null的键值(key)和值(value)。JDK 1.8中:当单个桶中元素个数大于等于8时,链表实现改为红黑树实现;当元素个数小于6时,变回链表实现。由此来防止hashCode攻击。

  • LinkedHashMap:保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的. 也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。LinkedHashMap采用的hash算法和HashMap相同,但是它重新定义了数组中保存的元素Entry,该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。

  • TreeMap:线程不同步,基于 红黑树 (Red-Black tree)的NavigableMap 实现,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

  • HashTable:线程安全,HashMap的迭代器(Iterator)是fail-fast迭代器。HashTable不能存储NULL的key和value。

13. Collection包结构,与Collections的区别。

(1)Collection 是单列集合

List 元素是有序的、可重复

有序的 collection,可以对列表中每个元素的插入位置进行精确地控制。

可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

可存放重复元素,元素存取是有序的。

List接口中常用类

l Vector:线程安全,但速度慢,已被ArrayList替代。

底层数据结构是数组结构

l ArrayList:线程不安全,查询速度快。

底层数据结构是数组结构

l LinkedList:线程不安全。增删速度快。

底层数据结构是列表结构

Set(集) 元素无序的、不可重复。

取出元素的方法只有迭代器。不可以存放重复元素,元素存取是无序的。

Set接口中常用的类

l HashSet:线程不安全,存取速度快。

它是如何保证元素唯一性的呢?依赖的是元素的hashCode方法和euqals方法。

l TreeSet:线程不安全,可以对Set集合中的元素进行排序。

它的排序是如何进行的呢?通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

(2)Map 是一个双列集合

|–Hashtable:线程安全,速度快。底层是哈希表数据结构。是同步的。

不允许null作为键,null作为值。

|–Properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。

是集合中可以和IO技术相结合的对象。(到了IO在学习它的特有和io相关的功能。)

|–HashMap:线程不安全,速度慢。底层也是哈希表数据结构。是不同步的。

允许null作为键,null作为值。替代了Hashtable.

|–LinkedHashMap: 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。

|–TreeMap:可以用来对Map集合中的进行排序.

(3)Collection 和 Collections的区别

Collection是集合类的上级接口,子接口主要有Set 和List。

Collections是针对集合类的一个帮助类,提供了操作集合的工具方法:一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

14. try catch finally,try里有return,finally还执行么?

答案:执行,并且finally的执行早于try里面的return

结论:

1、不管有木有出现异常,finally块中代码都会执行;

2、当try和catch中有return时,finally仍然会执行;

3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;

4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

举例:

情况1:try{} catch(){}finally{} return;

显然程序按顺序执行。

情况2:try{ return; }catch(){} finally{} return;

程序执行try块中return之前(包括return语句中的表达式运算)代码;

再执行finally块,最后执行try中return;

finally块之后的语句return,因为程序在try中已经return所以不再执行。

情况3:try{ } catch(){return;} finally{} return;

程序先执行try,如果遇到异常执行catch块,

有异常:则执行catch中return之前(包括return语句中的表达式运算)代码,再执行finally语句中全部代码,

最后执行catch块中return. finally之后也就是4处的代码不再执行。

无异常:执行完try再finally再return.

情况4:try{ return; }catch(){} finally{return;}

程序执行try块中return之前(包括return语句中的表达式运算)代码;

再执行finally块,因为finally块中有return所以提前退出。

情况5:try{} catch(){return;}finally{return;}

程序执行catch块中return之前(包括return语句中的表达式运算)代码;

再执行finally块,因为finally块中有return所以提前退出。

情况6:try{ return;}catch(){return;} finally{return;}

程序执行try块中return之前(包括return语句中的表达式运算)代码;

有异常:执行catch块中return之前(包括return语句中的表达式运算)代码;

则再执行finally块,因为finally块中有return所以提前退出。

无异常:则再执行finally块,因为finally块中有return所以提前退出。

**最终结论:**任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话。

如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的,编译器把finally中的return实现为一个warning。

15. Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。

(1)Java异常

Java中有Error和Exception,它们都是继承自Throwable类。

二者的不同之处

Exception:

  • 可以是可被控制(checked) 或不可控制的(unchecked)。

  • 表示一个由程序员导致的错误。

  • 应该在应用程序级被处理。

Error:

  • 总是不可控制的(unchecked)。

  • 经常用来用于表示系统错误或低层资源的错误。

  • 如何可能的话,应该在系统级被捕捉。

异常的分类

  • Checked exception: 这类异常都是Exception的子类。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。

  • Unchecked exception这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们不能通过client code来试图解决,所以称为Unchecked exception 。

16. Java面向对象的三个特征与含义。

答案:继承、多态、封装

17. Override和Overload的含义与区别。

答案:见JVM静态分派和动态分派

18. Interface与abstract类的区别。

答案:

1.abstract class 在Java中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。

2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的方法都是public abstract的。

3.抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。接口中定义的变量默认是public static final 型,且必须给其赋初值,所以实现类中不能重新定义,也不能改变其值。

4.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系,门和报警的关系。

5.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。

abstract class 和 interface 是 Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系。

19. Static class 与non static class的区别。

答案:

比对如下:

静态对象 非静态对象

拥有属性: 是类共同拥有的 是类各对象独立拥有的

内存分配: 内存空间上是固定的 空间在各个附属类里面分配

分配顺序: 先分配静态对象的空间 继而再对非静态对象分配空间,也就是初始化顺序是先静态再非静态.

Java静态对象到底有什么好处?

A,静态对象的数据在全局是唯一的,一改都改。如果你想要处理的东西是整个程序中唯一的,弄成静态是个好方法。 非静态的东西你修改以后只是修改了他自己的数据,但是不会影响其他同类对象的数据。

B,引用方便。直接用 类名.静态方法名 或者 类名.静态变量名就可引用并且直接可以修改其属性值,不用get和set方法。

C,保持数据的唯一性。此数据全局都是唯一的,修改他的任何一处地方,在程序所有使用到的地方都将会体现到这些数据的修改。有效减少多余的浪费。

D,static final用来修饰成员变量和成员方法,可简单理解为“全局常量”。对于变量,表示一旦给值就不可修改;对于方法,表示不可覆盖。

20. 锁的等级:方法锁、对象锁、类锁。

(1)基础

Java中的每一个对象都可以作为锁。

  • 对于同步方法,锁是当前实例对象

写在最后

以上就是我的面试过程,为了这次面试,也收集了很多的面试题,反正我已经面过了,那就免费分享出来吧!

需要的朋友:关注一下,然后点击这里即可免费领取

以下是部分面试题截图

方法。抽象类中可以有非抽象方法。接口中则不能有实现方法。

abstract class 和 interface 是 Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系。

19. Static class 与non static class的区别。

答案:

比对如下:

静态对象 非静态对象

拥有属性: 是类共同拥有的 是类各对象独立拥有的

内存分配: 内存空间上是固定的 空间在各个附属类里面分配

分配顺序: 先分配静态对象的空间 继而再对非静态对象分配空间,也就是初始化顺序是先静态再非静态.

Java静态对象到底有什么好处?

A,静态对象的数据在全局是唯一的,一改都改。如果你想要处理的东西是整个程序中唯一的,弄成静态是个好方法。 非静态的东西你修改以后只是修改了他自己的数据,但是不会影响其他同类对象的数据。

B,引用方便。直接用 类名.静态方法名 或者 类名.静态变量名就可引用并且直接可以修改其属性值,不用get和set方法。

C,保持数据的唯一性。此数据全局都是唯一的,修改他的任何一处地方,在程序所有使用到的地方都将会体现到这些数据的修改。有效减少多余的浪费。

D,static final用来修饰成员变量和成员方法,可简单理解为“全局常量”。对于变量,表示一旦给值就不可修改;对于方法,表示不可覆盖。

20. 锁的等级:方法锁、对象锁、类锁。

(1)基础

Java中的每一个对象都可以作为锁。

  • 对于同步方法,锁是当前实例对象

写在最后

以上就是我的面试过程,为了这次面试,也收集了很多的面试题,反正我已经面过了,那就免费分享出来吧!

需要的朋友:关注一下,然后点击这里即可免费领取

以下是部分面试题截图

Java程序员秋招三面蚂蚁金服,我总结了所有面试题,也不过如此

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mybatis源码解析:21道Java基础面试题及答案 的相关文章

随机推荐

  • 【网络】连接服务器失败(错误原因:Connection refused)| error 111: (111) Connection refused 原因分析

    服务器拒绝原因 xff1a connect 发起TCP连接请求被拒绝是由于目标服务器上无对应的监听套接字 xff08 IP amp amp PORT xff09 xff08 没有监听 无法创建 监听别的IP 端口 xff09 有几个可能性
  • 【C++软件开发】Linux相关面试题型

    一 xff1a Linux的I O模型介绍以及同步异步阻塞非阻塞的区别 xff08 超级重要 xff09 xff08 IO过程包括两个阶段 xff1a xff08 1 xff09 内核从IO设备读写数据和 xff08 2 xff09 进程从
  • 【C++】 STL详解

    目录 一 xff1a 泛型编程 二 xff1a 什么是STL 三 xff1a STL发展 四 xff1a STL组件 五 xff1a 容器 六 xff1a 类型成员 七 xff1a 适配器 八 xff1a 迭代器 九 xff1a 算法 十
  • 12.STM32freeRTOS---递归互斥信号量

    文章目录 前言一 创建递归互斥信号量二 释放递归互斥信号量三 获取递归互斥信号量四 官方例程总结 前言 递归互斥信号量可以看成是一个特殊的互斥信号量 对于互斥信号量 xff0c 获取了互斥信号量的任务就不能再次获取这个互斥信号量 xff0c
  • toString用法

    toString是定义在java lang Object的方法 xff0c 因此所有类都可以使用toString方法 xff0c 但是 toString方法本身返回的是地址信息而对于String Date File 包装类 等都重写了toS
  • 商医通项目总结

    一 项目概述 简介 尚医通即为网上预约挂号系统 xff0c 网上预约挂号是近年开展的一项便民就医服务 xff0c 旨在缓解看病难 挂号难的就医难题 网上预约挂号全面提供的预约挂号业务从根本上解决了这一就医难题 随时随地轻松挂号 xff0c
  • docker安装以及遇到的问题

    目录 一 docker安装 二 安装中可能碰到的问题 1 WSL 2 installation is incomplete 一 docker安装 docker官网下载 xff1a Docker Desktop Docker xff0c 下载
  • STM32MP157实验(三)——按键扫描和中断

    文章目录 按键扫描设计需求基础知识硬件设计STM32CubeIDE设计MX设置代码设计 实验结果 按键中断设计需求基础知识硬件设计STM32CubeIDE设计MX设置代码设计 总结 按键扫描 设计需求 通过按键扫描的方式实现 xff0c 按
  • 一篇博客实现嵌入式入门

    文章目录 前言最小系统和C语言最小系统原理图电源电路时钟电路复位电路调试 下载电路 嵌入式C语言基础知识数据类型const用法修饰变量修饰数组修饰指针修饰函数参数 作用域与static用法extern的用法volatile的用法struct
  • 驱动开发(一)——(单片机程序、Linux应用程序与驱动程序分析)

    文章目录 前言157准备工作配置交叉编译链编译内核编译解压glibc 单片机程序应用程序驱动程序三者的关系 前言 学习资料 xff0c 跟的韦东山老师的视频 xff0c 大家可以上百问网下载资料 百问网 我使用的开发板是STM32MP157
  • 驱动开发(五)——驱动设计思想(面向对象/分层/分离)

    文章目录 设计思想面向对象分层分离 实验手册资源驱动程序 led drv cled operations h芯片相关chip gpio cled resource h单板相关 board MP157 led c应用测试程序Makefile
  • 【shell】遇到错误退出set -e|set -u|set -x|shell 退出时执行|捕捉信号trap

    目录 遇到错误退出 简介和使用 set e 的陷阱 1 xff0c 管道命令 2 xff0c grep匹配不到会导致退出 shell退出时执行 接收信号 trap 用途 trap介绍 列出所有信号的数值和名字 trap的注意事项 遇到错误退
  • 我的世界java版官方网站,讲的太透彻了

    简介 基于SpringCloud Hoxton SR1 43 SpringBoot 2 2 4 RELEASE 的 SaaS型微服务脚手架 xff0c 具备用户管理 资源权限管理 网关统一鉴权 Xss防跨站攻击 自动代码生成 多存储系统 分
  • 深入理解java虚拟机pdf,GitHub已标星16k

    专题5 xff1a Java序列化 1 什么是java序列化 xff0c 如何实现java序列化 xff1f 2 保存 持久化 对象及其状态到内存或者磁盘 3 序列化对象以字节数组保持 静态成员不保存 4 序列化用户远程对象传输 5 Ser
  • 成功入职阿里月薪45K,附赠课程+题库

    网站基础知识 xff08 网站架构及其演变过程 43 常见协议和标准 43 DNS的设置 43 Java中Socket的用法 43 HTTP协议 43 详解Servlet 43 Tomcat分析 xff09 俯视 Spring MVC xf
  • 快醒醒吧!springcloud分布式事务面试题

    一 什么是架构和架构本质 在软件行业 xff0c 对于什么是架构 xff0c 都有很多的争论 xff0c 每个人都有自己的理解 此君说的架构和彼君理解的架构未必是一回事 因此我们在讨论架构之前 xff0c 我们先讨论架构的概念定义 xff0
  • 基于java实现直播,太牛了!

    美团技术一面20分钟 晚7点 xff0c 因为想到下周一才面试 xff0c 我刚准备出去打个羽毛球 xff0c 北京的电话就来了 面试官各种抱歉 xff0c 说开会拖延了 1 自我介绍 说了很多遍了 xff0c 很流畅捡重点介绍完 2 问我
  • java银行面试题目及答案,顺利拿到offer

    二 常见的并发问题 1 脏读 一个事务读取了另一个事务未提交的数据 2 不可重复读 一个事务对同一数据的读取结果前后不一致 两次读取中间被其他事务修改了 3 幻读 幻读是指事务读取某个范围的数据时 xff0c 因为其他事务的操作导致前后两次
  • 白嫖党最爱!javasocket服务端向客户端发消息

    一 公务员都不要35岁以上的 xff0c 何况大公司 这让很多人感到惶恐 xff0c 现在职场上有一种现象 xff1a 很多用人单位会在招聘信息上明确标注 xff0c 年龄需在35岁以下 为什么有经验 有人脉的职场中年人会如此遭 嫌弃 呢
  • Mybatis源码解析:21道Java基础面试题及答案

    10 HashMap和HashTable的区别 答案 xff1a Hashtable和HashMap类有三个重要的不同之处 1 第一个不同主要是历史原因 Hashtable是基于陈旧的Dictionary类的 xff0c HashMap是J