String 、StringBuffer 、StringBuild区别
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,
即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的
运行速度快慢为:StringBuilder > StringBuffer > String
Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,
而不进行创建和回收的操作,所以速度要比String快很多。
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
equals == 区别
== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。
比较的是真正意义上的指针操作。
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,
所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,
而Object中的equals方法返回的却是==的判断。
重写String的equals方法是怎么实现的
// 具体思想就是转变成基本类型去比较,如比较长度,比较每个字符
public boolean equals(Object anObject) {
// 如果是同一个对象
if (this == anObject) {
return true;
}
// 如果传递进来的参数是String类的实例
if (anObject instanceof String) {
String anotherString = (String)anObject;
// 字符串长度
int n = value.length;
// 如果长度相等就进行比较
if (n == anotherString.value.length) {
// 取每一个位置的字符
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 对于每一位置逐一比较
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
数组里有100个字符串,把这100个字符串拼接成一个字符串,有什么方法?
这里写代码片
创建线程的方式?
1、继承Thread类
(一)、重写run()方法,即线程执行体;
(二)、创建Thread子类的实例,也就是创建了线程对象;
(三)、启动线程,即调用线程的start()方法。
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
// 创建并启动线程
new MyThread().start();
}
}
2、实现Runnable接口
(一)、实现run()方法,即线程执行体;
(二)、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,
这个Thread对象才是真正的线程对象;
(三)、启动线程,即调用线程的start()方法。
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
3、实现Callable接口
(一)、实现call()方法,然后创建该实现类的实例;
(二)、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值;
(三)、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口);
(四)、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
public class Main {
public static void main(String[] args){
MyThread3 th=new MyThread3();
// jdk1.8使用Lambda表达式创建Callable对象
// 使用FutureTask类来包装Callable对象
FutureTask<Integer> future=new FutureTask<Integer>(
(Callable<Integer>)()->{
return 5;
}
);
// 实质上还是以Callable对象来创建并启动线程
new Thread(task,"有返回值的线程").start();
try{
// get()方法会阻塞,直到子线程执行结束才返回
System.out.println("子线程的返回值:"+future.get());
}catch(Exception e){
ex.printStackTrace();
}
}
}
4、线程池Executor
List和Set区别?
List?
ArrayList和LinkedList区别?
Set?
Map?
1.8之后HashMap的数组+链表结构为什么要加红黑树
IOC? AOP?
这里写代码片
RESTful风格?
这里写代码片
@RequestBody?
这里写代码片
项目中怎么捕获全局异常?
@ControllerAdvice
1、basePackages:应用在xx包
2、basePackageClasses:应用在xx类
3、assignableTypes:应用在加了@Controller的类
4、annotations:应用在带有xx注解的类或者方法
Demo:
@ControllerAdvice(basePackages={"com.springboot.controller"})
只捕捉com.springboot.controller包中的异常
@ControllerAdvice(basePackageClasses={TestController.class})
只捕捉TestController.class中的异常
@ControllerAdvice(assignableTypes={TestController.class})
只捕捉TestController.class中的异常
@ControllerAdvice(annotations=TestException.class)
只捕捉带有@TestException注解的类
现在spring用的什么版本?MySQL用什么版本?spring boot用什么版本?
bean的启动顺序怎么设置?
Linux查看端口占用的命令?
## 查看某一端口的占用情况,有则返回,没有则返回空
lsof -i:8080
## 用于显示tcp,udp的端口和进程等相关情况
netstat -tunlp
## 用于查看指定端口号的进程情况
netstat -tunlp |grep 8080
关闭防火墙?
1. 永久性生效
开启:chkconfig iptables on
关闭:chkconfig iptables off
2. 即时生效,重启后失效
开启:service iptables start
关闭:service iptables stop
Linux用的是什么版本?
CentOS 7
除了java以外还学了什么?
redis有几种数据类型?
String、list、set、sorted set、hash
缓存透传?
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,
就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,
并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
redis有什么使用场景?
字典、权限、计数器、频繁查找的数据
redis到MySQL数据库怎么设计?
在并发不高的情况下,
读操作优先读取redis,不存在的话就去访问MySQL,并把读到的数据写回Redis中;
写操作的话,直接写MySQL,成功后再写入Redis。
在并发高的情况下,
读操作和上面一样,写操作是异步写,写入Redis后直接返回,然后定期写入MySQL。
Demo:
1.当更新数据时,如更新某商品的库存,当前商品的库存是100,现在要更新为99,
先更新数据库更改成99,然后删除缓存,发现删除缓存失败了,这意味着数据库存的是99,
而缓存是100,这导致数据库和缓存不一致。
解决方法:
这种情况应该是先删除缓存,然后再更新数据库,如果删除缓存失败,
那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,
那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。
2.在高并发的情况下,如果当删除完缓存的时候,这时去更新数据库,但还没有更新完,
另外一个请求来查询数据,发现缓存里没有,就去数据库里查,还是以上面商品库存为例,
如果数据库中产品的库存是100,那么查询到的库存是100,然后插入缓存,插入完缓存后,
原来那个更新数据库的线程把数据库更新为了99,导致数据库与缓存不一致的情况
解决方法:
遇到这种情况,可以用队列的去解决这个问,创建几个队列,如20个,
根据商品的ID去做hash值,然后对队列个数取摸,当有数据更新请求时,
先把它丢到队列里去,当更新完后在从队列里去除,如果在更新的过程中,
遇到以上场景,先去缓存里看下有没有数据,如果没有,
可以先去队列里看是否有相同商品ID在做更新,如果有也把查询的请求发送到队列里去,
然后同步等待缓存更新完成。
要是插入数据库失败了怎么办?
同上
正好另外一个线程去读redis,读到错误的数据呢?
同上
docker你知道吗?
docker查看容器的命令?
docker ps -a
zookeeper是做什么的?
是一种分布式协调服务,相当于人类的大脑,负责协调,联络子系统的功能。
它的选举机制?
实现多线程的方式?
Thread类和Runnable接口有什么关系?
Thread实现Runnable接口
创建对象有什么方式?
import java.io.Serializable;
import java.util.Objects;
public class User implements Cloneable,Serializable {
private static final long serialVersionUID = -6754389903840065949L;
private String userName;
public User(){
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public boolean equals(Object o) {
if (this == o){ return true;}
if (o == null || getClass() != o.getClass()) {return false;}
User user = (User) o;
return
Objects.equals(userName, user.userName) ;
}
@Override
public int hashCode() {
return Objects.hash( userName);
}
@Override
public Object clone(){
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
1、new 关键字 构造器
User user1=new User();
2、反射
User user2= (User) Class.forName("com.zhao.bean.User").newInstance();
3、反序列化,类必须实现Serializable 接口,并定义serialVersionUID
ObjectInputStream in = new ObjectInputStream (new FileInputStream("user.txt"));
User user3 = (User)in.readObject();
4、使用Clone的方法,类必须实现Cloneable接口
User user4= (User) user3.clone();
5、使用Constructor类的newInstance方法
Constructor<User> constructor=User.class.getConstructor();
User user5=constructor.newInstance();
6、最特殊的创建对象
String str="abc";
读过什么源码?
HashMap和Hashtable有什么区别?
1、前者线程不安全,后者线程安全
2、前者key和value可以为null,后者不允许
3、前者数组默认大小为16,当达到阈值时进行扩容,扩容方式为old*2,后者数组默认大小为11,扩容方式为old*2+1
Hashtable和ConcurrentHashMap区别?
1、前者线程安全使用的是整个范围的锁,后者使用的是分段锁,可同时进行多个修改操作
2、前者put和get都是线程同步的,后者get不需要锁,put需要锁
ArrayList初始放进去15个对象,那我再放一个对象进去会怎么样?
会扩容,新的容量=旧容量*1.5,即新容量为15*1.5,此时容量为23