目录
四、String、StringBuffer、StringBuilder的区别
六、hashSet如何检查重复(可以说明为什么要有hashCode)
一、Dubbo中zookeeper做注册中心,如果集群都挂掉,发布者跟订阅者之间还能通信吗?
Java基础
一、面向对象、面向过程的定义
-
面向对象更注重事情的参与者(即对象)、以及各自需要做些什么;
-
面向过程的优点是性能比面向对象高,因为类调用时需要实例化,开销比较大,更消耗资源;缺点是没有面向对象易维护、易复用、易扩展;
-
面向对象的优点是易维护、易复用、易扩展,由于面向对象的四个特性,可以设计出低耦合的系统,使得系统更灵活、更易于维护;缺点是性能比面向过程低。
二、Java的四个基本特性
- 抽象:把生活中的某一类东西提取出来,通常称为类或者接口,包括数据抽象(对象属性)和过程抽象(对象行为特征);
- 封装:将客观事物封装成抽象的类,并且类可以将自己的数据和方法对不可信的类或者对象封装隐藏,分为属性的封装和方法的封装;
- 继承:对有着共同特性的多类事物进行再抽象成一个类,这个类就是多类事物的父类,父类的意义在于抽取多类事物的共性;
- 多态:允许不同类的对象对同一消息做出响应。
三、重载、重写的区别
- 重载:发送在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,返回值和访问修饰符可以不同;
- 重写:发送在父子类中,方法名、参数列表必须相同,返回值、抛出的异常小于等于父类,访问修饰符大于等于父类。如果父类访问修饰符是private,则子类中不是重写。重写使用@Override注解,可以让编译器帮忙检查是否满足条件。
四、String、StringBuffer、StringBuilder的区别
- 可变性:String类中使用字符数组保存字符串,private final char value[ ],所以string对象是不可变的;StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[ ] value,这两种对象是可变的;
- 线程安全性:String中的对象是不可变的,也就可以理解为常量,所以线程安全。AbstractStringBuilder是StringBuilder和StringBuffer的公共父类,定义了一些字符串的基本操作,例如append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以线程安全;StringBuilder没有对方法加同步锁,所以是非线程安全的;
- 在性能方面,StringBuilder优于StringBuffer,StringBuffer优于String;
- 经常需要改变字符串内容时使用StringBuilder,多线程使用共享变量时使用StringBuffer。
五、hashCode、equals
- equals相等,hashCode必定相等;hashCode相等,equals可能不相等;
- equals方法被覆盖过,则hashCode方法也必须被覆盖。
六、hashSet如何检查重复(可以说明为什么要有hashCode)
- 对象加入hashSet时,会先计算对象的hashCode值,判断对象加入的位置,看对应的位置是否有值,如果没有,hashSet会假定对象没有重复出现,如果有,会调用equals来检查两个对象是否真的相同。如果相同,hashSet就不会让加入操作成功;如果不同,就会重新散列到其他位置,大大减少equals的次数,提高了执行速度。
七、抽象类、接口的区别是什么
- 抽象类是对整个类进行抽象,包括属性、行为,是自底向上抽象而来的;接口是对行为抽象,是自顶向下设计的;
- 抽象类可以存在普通成员函数;接口只能存在抽象方法,jdk1.8之后可以添加默认方法;
- 抽象类中的成员变量可以是各种类型的;接口中的成员变量时public static final类型的;
- 抽象类只能继承一个;接口可以实现多个;
- 抽象类的设计目的是代码的复用,不同类具有某些相同行为且其中一部分行为实现方式一致时,可以让这些类派生于一个抽象类;接口的设计目的是对类的行为进行约束,约束行为的有无但是不对如何实现行为进行限制;
- 抽象类是对类本质的抽象,表达的是is a的关系,包含并实现了子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现;接口是对行为抽象,表达的是like a的关系,其核心是定义行为,即实现类可以做什么,至于实现类主体是谁、是如何实现的,接口不关心;
- 关注事物的本质时,用抽象类;关注操作时,用接口;
- 抽象类不能被实例化,只能被继承。
七、volatile关键字
- 处理器为了提高程序运行效率,可能会对输入代码进行优化,但是不能保证程序中的各个与的执行先后顺序同代码中一致,但是会保证程序最终执行结果和代码顺序执行的结果一致;
- volatile可以保证可见性:当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的;
- volatile可以禁止指令重排:volatile修饰的变量,可以保证变量赋值操作的顺序与程序代码中的执行顺序一致;
- 基于volatile变量的运算在并发下不一定是安全的。
八、static关键字
- 可以修饰内部类、方法、变量、代码块;
- 修饰的类是静态内部类,可以访问外部类所有的静态变量和方法,即使是private也一样,可以定义静态变量、方法、构造方法等;
- 修饰的方式是静态方法,表示该方法属于当前类,而不属于某个对象,静态方法不能被重写,可以直接使用类名来调用static方法中不能使用this或者super关键字;
- 修饰的变量是静态变量(类变量),静态变量被所有实例共享,不会依赖于对象,在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存;
- 修饰的代码块是静态代码块,通常用来做程序优化,整个静态代码块中的代码只会在整个类加载的时候执行一次,静态代码块可以有多个,按先后顺序执行。
九、静态变量、实例变量
- 静态变量也称为类变量,类的所有实例都共享静态变量,可通过类名直接访问,静态变量在内存中只存在一份;
- 实例变量必须依存于某一个实例,只能通过对象才能进行访问,与实例同生共死。
十、get和post的区别
- get是用来从服务器上获取数据,而post是用来向服务器上传递数据;
- get将表单中的数据按照variable=value的形式,添加到action所指向的URL后,并且两者使用“?”连接,各变量之间用“&”连接;post是将表单中的数据放在form数据体中,按照变量和值相对应的方式传递到action所指向URL;
- get是不安全的,在传输过程中,数据被放在请求的URL中;post的所有操作对用户来说不可见;
- get的传输的数据量小,主要是受URL长度限制;post可以传输大量数据,所以上传文件只能使用post;
- get限制form表单的数据集必须是ASCII字符;post支持整个ISO10646字符集;
- get是form的默认方式。
十一、final关键字
- final修饰的类不可以被继承;
- final修饰的方法不可以被重写;
- final修饰的变量不可以被改变,如果修饰引用,那么表示引用不可变,引用指向的内容可变;
- final修饰的方法,JVM会尝试将其内联,提高运行效率;
- final修饰的常量,在编译期会存入常量池。
十二、HashMap和Hashtable的区别
- 二者都实现了Map接口;
- HashMap没有排序,允许null键(一个)和null值(多个),Hashtable不允许;
- HashTable继承自Dictionary类,HashMap是Map接口的实现类;
- HashTable的方法是Synchronize的,而HashMap不是,在多个线程访问HashTable时,不需要实现同步,而HashMap需要提供;
- HashMap把HashTable的contains方法去掉了,改成了containsvalue和containsKey;
- HashTable和HashMap采用的hash/rehash算法大致一样,所以性能没有很大差异。
十三、数据类型
- byte/8、char/16、short/16、int/32、float/32、long/64、double/64
- boolean:只有true和false,可以使用1bit存储。
十四、String
- String被声明为final,不可被继承;
- String中的常用方法如下:
方法名 | 作用 |
compareTo(String s) | 比较两个字符串 |
equals(Object obj) | 判断内容是否相等 |
endsWith(String s) | 判断字符串是否以s结尾 |
contains(String s) | 判断字符串是否包含s |
length() | 获取字符串长度 |
split(String split) | 分割字符串 |
trim() | 返回消除空格后的字符串 |
十五、异常
- 所有异常的顶级父类是Throwable,Throwable下面有两个子类Exception和Error;
- Exception分为两类:一类是受检异常,需要用try...catch...语句进行捕获,并且可以从异常中恢复;一类是非受检异常,是程序运行时错误,例如除0;
- 常见异常如下:
(1)java.lang.NullPointerException,调用了未经初始化的对象或者是不存在的对象;
(2)java.lang.ClassNotFoundException,指定的类不存在;
(3)java.lang.NumberFormatException,字符串转换为数字异常;
(4)java.lang.IndexOutOfBoundsException,数组下标越界异常;
(5)java.lang.ArithmeticException,数学运算异常。
十六、Java和C++的区别
- Java是面向对象,C++为了兼容C,既支持面向对象也支持面向过程;
- Java通过虚拟机可以实现跨平台,C++依赖于特定平台;
- Java没有指针,其引用可以理解为安全指针,C++有指针;
- Java支持自动垃圾回收,C++需要手动回收;
- Java不支持多继承,只能实现多接口,C++支持多继承。
十七、List、Set
- List:有序,可重复,允许多个null元素,可以使用Iterator取出所有元素,也可以使用get方法获取指定下标的元素;
- Set:无序,不可重复,最多允许一个null元素,只能使用Iterator取元素。
十八、ArrayList、LinkedList
- ArrayList基于动态数组,支持快速随机访问。数组默认大小是10。
- LinkedList基于链表,适合做数据的插入以及删除操作,但不适合查询,需要逐一遍历。
十九、HashMap的底层实现
- 底层是数组+链表实现;
- jdk1.8之后,链表高度达到8、数组长度超过64,链表就会转为红黑树。
(1)计算key的hash值,二次hash然后对数组长度取模,对应到数组下标;
(2)如果没有产生hash冲突(下标位置没有元素),直接创建节点存入数组;
(3)如果产生hash冲突,先进行equal比较,相同则取代,不同,则判断链表高度插入链表,高度达到8,并且数组长度到64,则转变为红黑树,长度低于6则将红黑树转回链表;
(4)key为null,存在下标为0的位置。
线程、并发相关
一、使用线程的方法
- 实现Runnable接口;
- 实现Callable接口;
- 继承Thread类。
二、线程的生命周期
- 线程通常有五种状态:创建、就绪、运行、阻塞、死亡;
- 阻塞态又分为三种:
(1)等待阻塞:执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入等待池中,进入这个状态不能被自动唤醒,必须依靠其他线程调用notify或者notifyAll才能唤醒;
(2)同步阻塞:运行的线程在获取对象的同步锁时,若同步锁被占用,则JVM会把该线程放入锁池中;
(3)其他阻塞:执行sleep或者join方法,或者发出I/O请求时,JVM就会将该线程置为阻塞态。当sleep状态超时、join等待线程终止或者超时、或者I/O请求处理完毕时,线程重新进入就绪态。
三、sleep、wait的区别
- sleep是Thread类的静态本地方法,wait是Object类的本地方法;
- sleep方法不会释放lock,wait会释放lock,并加入到等待队列中;
- sleep方法不依赖于synchronized,wait需要依赖synchronized;
- sleep不需要被唤醒,wait需要;
- sleep一般用于当前线程休眠或者轮循暂停操作,wait则多用于多线程之间的通信;
- sleepe会让出CPU执行时间且强制上下文切换,wait则不一定。
四、并发的三大特性
- 原子性:指在一个操作中CPU不可以在中途暂停然后再调度,即不被中断操作,要么全部执行,要么都不执行。关键字:synchronized;
- 可见性:当多个线程访问到同一个变量时,一个线程修改了值,其他线程都能立即看到修改的值。关键字:volatile、synchronized、final;
- 有序性:虚拟机在进行代码编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按照写的代码的顺序执行,有可能进行重排序。实际上,有些代码进行重排序之后,虽然对变量的值没有造成影响,但有可能出现线程安全问题。关键词:volatile、synchronized。
五、Synchronized的原理
Synchronized是由JVM实现的一种实现互斥同步的一种方式,查看被Synchronized修饰过的代码块,在编译前后被编译器生成了monitorenter和monitorexit两个字节码指令。在虚拟机执行到monitorenter指令时,首先要尝试获取对象的锁,如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;执行monitorexit指令时将锁计数器-1;当计数器为0时,锁被释放。如果获取对象失败,那么当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。
Synchronized是通过在对象头设置标记,达到了获取锁和释放锁的目的。
六、什么是CAS
CAS(Compare and Swap,比较并交换),涉及到三个操作数:内存值、预期值、新值。当且仅当预期值和内存值相等时才将内存值修改为新值。
处理逻辑是首先检查某块内存的值是否跟之前读取的一样,如果不一样则表示期间此内存值已经被别的线程修改了,舍弃本次操作,否则说明期间没有其他线程对此内存值操作,可以将新值设置给此块内存。
CAS具有原子性。
Spring Boot
一、什么是Spring Boot
现如今,启动一个新的Spring项目,必须添加构建路径或者添加maven依赖关系,配置应用程序服务器,添加Spring配置,因此开始一个新的Spring项目需要从头开始做所有事情。
Spring Boot建立在现有的Spring框架之上,因此,Spring Boot可以帮助我们以最少的工作量,更健壮地使用现有Spring功能。
二、Spring Boot有哪些优缺点
- 优点:
(1)快速构建项目;
(2)对主流开发框架的无配置集成;
(3)项目可独立运行,无须外部依赖Servlet容器;
(4)提供运行时的应用监控;
(5)极大提高了开发、部署效率;
(6)与云计算天然集成。
- 缺点:
(1)版本迭代速度很快,一些模块改动很大;
(2)不需要自己做配置,报错时定位困难;
(3)网上现成解决方案比较少。
三、什么是Swagger
Swagger广泛应用于可视化API,使用Swagger UI为前端开发人员提供在线沙箱。Swagger是用于生成RestFul Web服务的可视化表示的工具,规定和完整框架实现。它使文档能够以与服务器相同的速度更新。当通过Swagger正确定义时,消费者可以使用最少量的实现逻辑来理解远程服务并与其进行交互,因此,Swagger消除了调用服务时的猜测。
Dubbo
一、Dubbo中zookeeper做注册中心,如果集群都挂掉,发布者跟订阅者之间还能通信吗?
可以;启动Dubbo时,消费者会从zookeeper拉取注册的生产者的地址接口等数据,缓存到本地。每次调用时按照本地存储的地址进行调用。注册中心对等集群,任意一台宕机,将会切换到另一台;注册中心全部宕机,仍旧能通过本地缓存进行通信。
二、Dubbo服务负载均衡策略
- Random LoadBalance:随机;
- RoundRobin LoadBalance:轮循;
- LeastActive LoadBalance:最少活跃调用数;
- ConsistentHash LoadBalance:一致性Hash。