JAVA面试汇总

1.JavaSE

1.1 String

面试题1: 字符串常用API

1.反转字符串:reverse();被反转的字符串应该是StringBuffer类型
2.替换字符串 replaceAll(“被替换的字符串”,“替换字符串”):
3.查找字符在字符串中的位置:indexOf();要注意是从0开始数的。
4.截取字符串:substring(0,5);//从第0个开始截取一直到第4个,不包括第五个
5.字符串大小写转换:小写转大写:toUpperCase();大写转小写:toLowerCase();
6.去掉字符串中的空格trim();
7.endsWith()和startsWith():判断字符串是不是以某个字符开头和结尾

1.2 面向对象

面试题1: 面向对象三大特征讲一下

封装:将对象运行所需的资源封装在程序对象中——基本上,是方法和数据。对象是“公布其接口”。其他附加到这些接口上的对象不需要关心对象实现的方法即可使用这个对象。这个概念就是“不要告诉我你是怎么做的,只要做就可以了。”对象可以看作是一个自我包含的原子。对象接口包括了公共的方法和初始化数据

继承:说到继承并不太陌生,继承可以使得子类具有父类的各种的公有属性和公有方法。而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。

多态:实现多态的三个条件(前提条件,向上转型、向下转型)
1、继承的存在;(继承是多态的基础,没有继承就没有多态)
2、子类重写父类的方法。(多态下会调用子类重写后的方法)
3、父类引用变量指向子类对象。(涉及子类到父类的类型转换)
向上转型 Student person = new Student()
将一个父类的引用指向一个子类对象,成为向上转型,自动进行类型转换。此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,而不是父类的方法此时通过父类引用变量无法调用子类特有的方法。
向下转型 Student stu = (Student)person;
将一个指向子类对象的引用赋给一个子类的引用,成为向下转型,此时必须进行强制类型转换。向下转型必须转换为父类引用指向的真实子类类型,,否则将出现ClassCastException,不是任意的强制转换
向下转型时可以结合使用instanceof运算符进行强制类型转换,比如出现转换异常—ClassCastException

面试题2: hashcode()和equals()之间的关系?

equals()是判读两个Set是否相等[前提是equals()在类中被覆盖]。==决定引用值是否指向同一对象。
1、当向集合set中增加对象时,首先计算要增加对象的hashCode码,根据该值来得到一个位置来存放当前的对象,当在该位置没有一个对象存在的话,那么集合set认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,再进行一次散列,将该对象放到散列后计算出的新地址里,如果equals方法返回true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。
2、当重写equals方法时,必须要重写hashCode方法。在java的集合中,判断两个对象是否相等的规则是:
1),判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等,完毕 ; 如果相等,转入2
2),判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
可见hashcode()相等时,equals()方法也可能不等。

面试题3: 说说Object类下的方法。

1.Object()
这个没什么可说的,Object类的构造方法。(非重点)
2.registerNatives()
为了使JVM发现本机功能,他们被一定的方式命名。例如,对于java.lang.Object.registerNatives,对应的C函数命名为Java_java_lang_Object_registerNatives。
通过使用registerNatives(或者更确切地说,JNI函数RegisterNatives),可以命名任何你想要你的C函数。(非重点)
3.clone()
clone()函数的用途是用来另存一个当前存在的对象。只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。(注意:回答这里时可能会引出设计模式的提问)
4.getClass()
final方法,用于获得运行时的类型。该方法返回的是此Object对象的类对象/运行时类对象Class。效果与Object.class相同。(注意:回答这里时可能会引出类加载,反射等知识点的提问)
5.equals()
equals用来比较两个对象的内容是否相等。默认情况下(继承自Object类),equals和==是一样的,除非被覆写(override)了。(注意:这里可能引出更常问的“equals与==的区别”及hashmap实现原理的提问)
6.hashCode()
该方法用来返回其所在对象的物理地址(哈希码值),常会和equals方法同时重写,确保相等的两个对象拥有相等的hashCode。(同样,可能引出hashmap实现原理的提问)
7.toString()
toString()方法返回该对象的字符串表示,这个方法没什么可说的。
8.wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。(引出线程通信及“wait和sleep的区别”的提问)
9.wait(long timeout)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。(引出线程通信及“wait和sleep的区别”的提问)
10.wait(long timeout, int nanos)
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。(引出线程通信及“wait和sleep的区别”的提问)
11.notify()
唤醒在此对象监视器上等待的单个线程。(引出线程通信的提问)
12.notifyAll()
唤醒在此对象监视器上等待的所有线程。(引出线程通信的提问)
13.finalize()
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。(非重点,但小心引出垃圾回收的提问)

1.3 泛型

面试题1: 解释一下泛型擦除

Java 的泛型使用了类型擦除机制,这个引来了很大的争议,以至于 Java 的泛型功能受到限制,只能说是”伪泛型“。什么叫类型擦除呢?简单的说就是,类型参数只存在于编译期,在运行时,Java 的虚拟机 ( JVM ) 并不知道泛型的存在。

面试题2: ArrayList和LinkedList可以互转吗

ArrayList与类LinkedList强制数据类型转换是不行的,因为类ArrayList与类LinkedList不是父子类关系。
但可以通过构造方法转换 ArrayList arrayList = new ArrayList(); ……对arrayList对象添加数据 LinkedList linkedList = new LinkedList(arrayList);或 LinkedList linkedList = new LinkedList(); ……对linkedList对象添加数据 ArrayList arrayList = new ArrayList(linkedList);
linked可以向任意位置插入数据,插入位置后的数据,自动后移
list.addFirst(objects);

1.4 集合

面试题1: Arraylist和hashMap正在遍历的时候插入有什么问题

代码对ArrayList遍历都是通过Iterator,而添加ArrayList的元素是通过ArrayList的add方法,所以会使expectedModCount与modCount不相等,抛出异常
hashmap会先用hash函数处理键值,让其能够均匀分布到map中,这就会导致map中key的顺序并非插入时候的顺序。

面试题2: HashTable 和 hashMap的区别

1.继承的父类不同
Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口。
2.线程安全性不同
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
3.是否提供contains方法
HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey,因为contains方法容易让人引起误解。
Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
4.key和value是否允许null值
Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
5.内部实现使用的数组初始化和扩容方式不同
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

面试题3: 聊聊你对数据结构的理解

数据结构可分为:线性结构和非线性结构。
线性结构:
线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系
线性结构有两种不同的存储结构,即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
线性结构常见的有:数组、队列、链表和栈.
非线性结构:
非线性结构包括:二维数组,多维数组,广义表,树结构,图结构
非线性结构可以继续分:集合、树形结构、图状结构
集合结构: 除了同属于一种类型外,别无其它关系。
树形结构: 元素之间存在一对多关系。常见类型有:树(有许多特例:二叉树、平衡二叉树、查找树等)
图形结构: 元素之间存在多对多关系,图形结构中每个结点的前驱结点数和后续结点多个数可以任意。
存储结构表示数据在计算机中的表现形式:
顺序存储结构:顺序存储结构将数据存储在地址连续的存储单元里。
链接存储结构:链式存储结构将数据存储在任意的存储单元里,通过保存地址的方式找到相关联的数据元素。
索引存储结构;在存储数据的同时,简历数据的索引数据,方便对数据进行查询;
散列存储结构:通过散列函数对关键字进行计算算出元素的存储地址

面试题4: ArrayList和LinkedList使用场景

使用场景:
(1)如果应用程序对数据有较多的随机访问,ArrayList对象要优于LinkedList对象;
(2) 如果应用程序有更多的插入或者删除操作,较少的随机访问,LinkedList对象要优于ArrayList对象;
(3) 不过ArrayList的插入,删除操作也不一定比LinkedList慢,如果在List靠近末尾的地方插入,那么ArrayList只需要移动较少的数据,而LinkedList则需要一直查找到列表尾部,反而耗费较多时间,这时ArrayList就比LinkedList要快。

面试题5: 说下hashmap原理,如何决定HashMap和TreeMap的使用?

介绍
TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap<K,V>的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在Map中插入、删除和定位元素。
结论
如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。

面试题6: 为什么hashMap的数组的空间长度为2的整数次幂

为了加快哈希计算以及减少哈希冲突

为什么可以加快计算?
我们都知道为了找到 KEY 的位置在哈希表的哪个槽里面,需要计算 hash(KEY) % 数组长度
但是!% 计算比 & 慢很多
所以用 & 代替 %,为了保证 & 的计算结果等于 % 的结果需要把 length 减 1
也就是 hash(KEY) & (length - 1)
这个 hash(KEY) 没什么可说的,调用 Object 里面的 native 方法完成计算,一般返回的是一个整数,至于是偶数还是奇数就不一定了
还有一个有意思的事就是:因为扩容为 2 的倍数,根据 hash 桶的计算方法,元素哈希值不变而通过 % 计算的方式会因为 length 的变化导致计算出来的 hash 桶的位置不断变化。数据一致在漂移,影响性能!!
为什么可以减少冲突?
假设现在数组的长度 length 可能是偶数也可能是奇数
length 为偶数时,length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性。
而如果 length 为奇数的话,很明显 length-1 为偶数,它的最后一位是 0,这样 hash & (length-1) 的最后一位肯定为 0,即只能为偶数,这样任何 hash 值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间
因此,length 取 2 的整数次幂,是为了使不同 hash 值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列。

面试题7: 集合有哪些

Java集合类型主要有3种:set(集)、list(列表)和map(映射)。
Collection、Set和List的区别如下:
Collection对象之间没有指定的顺序,允许有重复元素和多个null元素对象;它是Set和List接口的父类,是一种最通用型的集合接口;
Set各个元素对象之间没有指定的顺序,不允许有重复元素,最多允许有一个null元素对象;
List各个元素对象之间有指定的顺序,允许重复元素和多个null元素对象;

1.5 多线程

面试题1: 创建线程的方式

一、继承Thread类创建
  通过继承Thread并且重写其run(),run方法中即线程执行任务。创建后的子类通过调用 start() 方法即可执行线程方法。
  通过继承Thread实现的线程类,多个线程间无法共享线程类的实例变量。(需要创建不同Thread对象,自然不共享)
二、 通过Runnable接口创建线程类
   该方法需要先 定义一个类实现Runnable接口,并重写该接口的 run() 方法,此run方法是线程执行体。接着创建 Runnable实现类的对象,作为创建Thread对象的参数target,此Thread对象才是真正的线程对象。通过实现Runnable接口的线程类,是互相共享资源的。
三、 使用Callable和Future创建线程
  从继承Thread类和实现Runnable接口可以看出,上述两种方法都不能有返回值,且不能声明抛出异常。而Callable接口则实现了此两点,Callable接口如同Runable接口的升级版,其提供的call()方法将作为线程的执行体,同时允许有返回值。
  但是Callable对象不能直接作为Thread对象的target,因为Callable接口是 Java 5 新增的接口,不是Runnable接口的子接口。对于这个问题的解决方案,就引入 Future接口,此接口可以接受call() 的返回值,RunnableFuture接口是Future接口和Runnable接口的子接口,可以作为Thread对象的target 。并且, Future 接口提供了一个实现类:FutureTask 。
  FutureTask实现了RunnableFuture接口,可以作为 Thread对象的target。
总结
  通过上述三种方式,其实可以归为两类:继承类和实现接口两种方式。相比继承, 接口实现可以更加灵活,不会受限于Java的单继承机制。并且通过实现接口的方式可以共享资源,适合多线程处理同一资源的情况。线程知识丰富繁杂,更多细节还需努力学习掌握。

面试题2: 说下多线程,锁知道吗?

(1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
(2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
(3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
(4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
(5)Lock可以提高多个线程进行读操作的效率。
(6)在JDK1.5中,synchronized是性能低效的。因为这是一个重量级操作,它对性能最大的影响是阻塞式的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。相比之下使用Java提供的Lock对象,性能更高一些。但是,JDK1.6,发生了变化,对synchronize加入了很多优化措施,有自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。因此。提倡优先考虑使用synchronized来进行同步。

面试题3: 多线程是什么,谈谈你对线程的理解

进程:是一个正在执行中的程序
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程,至少有一个控制单元。
java VM 启动的时候会有一个进程java.exe,该进程中至少有一个线程负责Java程序的执行
而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
JVM启动不止一个线程,还有负责垃圾回收机制的线程。
任何一个代码执行的时候都会进内存,线程的控制单元依次往下执行,而负责执行的代码都在线程。
线程的出现让我们的程序有同时出现的效果,多线程能帮我们提高效率,目的在于能使多个代码同时运行。
线程是程序中的执行线程,Java虚拟机允许应用程序并发的运行多个线程
每个线程都有一个优先级,高优先级线程的执行优于低优先级的线程。当某个线程中运行的代码创建一个新Thread对象时,
该线程的初始优先级被设定为创建线程的优先级。

面试题4: 多线程状态,wait与sleep区别

(1)首先应该明确sleep方法是Thread类中定义的方法,而wait方法是Object类中定义的方法。
(2)sleep方法必须人为地为其指定时间。wait方法既可以指定时间,也可以不指定时间。
(3)sleep方法时间到,线程处于临时阻塞状态或者运行状态。wait方法如果没有被设置时间,就必须要通过notify或者notifyAll来唤醒。
(4)sleep方法不一定非要定义在同步中。wait方法必须定义在同步中。
(5)当二者都定义在同步中时,线程执行到sleep,不会释放锁。线程执行到wait,会释放锁。

面试题5: 线程池的实现原理分析

线程池一般时间都只会开启核心线程数量的线程,而当任务队列满了之后,线程池会启用非核心线程池区(这里描述只是逻辑分区,实际上线程之间都是同等地位),创建线程并执行任务;而当任务队列中任务变少,有些线程开始闲置,闲置时间达到设置的keepAliveTime后,线程池会注销回收这些闲置线程,直到线程数量恢复核心线程数

面试题6: volatile是什么意思?干什么用的?

volatile定义
​ 是java虚拟机提供的轻量级的同步机制。保证可见性,不保证原子性,禁止指令重排。
​ jmm
volatile的可见性
​ 由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(也叫栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行。操作过程有三步。
首先要将变量从主内存拷贝到自己的工作内存空间。
然后对变量进行操作。
操作完成后再将变量写回主内存,不能直接操作主内存中的变量。
各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下。
volatile的不保证原子性
​ 原子性定义:不可分割,完整性,也就是说某个线程正在做某个具体业务时,中间不可以被加塞或者被分割,需要具体完成,要么同时成功,要么同时失败。
如何让volatile保证原子性
最简单的方法,加sync的锁。
可以使用JUC下面的原子包装类。
volatile禁止指令重排
单线程环境里面确保最终执行结果和代码顺序的结果一致
处理器在进行重排序时,必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

面试题7: 线程池七大参数,线程池作用

一、corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
二、maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
三、keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
四、unit 空闲线程存活时间单位
keepAliveTime的计量单位
五、workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
六、threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
七、handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:
①CallerRunsPolicy
该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
②AbortPolicy
该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
③DiscardPolicy
该策略下,直接丢弃任务,什么都不做。
④DiscardOldestPolicy
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

1.6 设计模式

一、设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
二、设计模式的六大原则
1、开闭原则(OpenClose Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(LiskovSubstitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(DependenceInversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(InterfaceSegregation Principle)
这个原则的意思是࿱

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java微服务面试问题非常广泛,以下是一些可能会被问到的问题和回答: 1. 微服务是什么?为什么使用微服务架构? 微服务是一种软件架构模式,它将一个大型应用程序拆分成一组小型、相互独立的服务。每个服务都有自己的业务逻辑和数据存储,可以独立开发、部署和扩展。微服务架构具有许多优点,包括灵活性、可扩展性、模块化和团队自治。 2. 在微服务中如何处理服务之间的通信? 微服务之间的通信可以使用RESTful API或消息队列来实现。RESTful API是一种基于HTTP协议的轻量级通信机制,它可以通过GET、POST、PUT和DELETE等HTTP方法来发送和接收数据。消息队列则可以实现异步通信,并提供更高的可靠性和可伸缩性。 3. 如何保证微服务架构的可靠性和容错性? 为了保证可靠性和容错性,可以使用负载均衡、故障转移和容错机制来处理微服务的请求。负载均衡可以将请求分发到多个服务实例上,以提高性能和可伸缩性。故障转移可以在某个服务实例失败时自动切换到备用实例。容错机制可以处理网络故障和服务不可用的情况。 4. 如何测试微服务? 在测试微服务时,可以使用单元测试、集成测试和端到端测试来确保每个服务的功能和性能都达到预期。单元测试可以测试单个服务的业务逻辑。集成测试可以测试多个服务之间的协作和通信。端到端测试可以模拟真实用户场景,并测试整个系统的功能和性能。 5. 在微服务中如何处理数据一致性? 于微服务之间的数据存储是分散的,确保数据一致性可能会成为一个挑战。可以使用分布式事务或事件驱动的架构来处理数据一致性。分布式事务可以确保多个服务在进行数据更新时的一致性。事件驱动的架构则可以通过发布和订阅事件来实现数据同步。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值