【零散知识点总结5】

大部分来源于网络

《零散知识点总结1》
该文章涉及:Dubbo、HTTP和HTTPS、Mybatis、Hibernate、 Zookeeper、Kafka、Elasticsearch、Redis

《零散知识点总结2》
该文章涉及:MySQL、Java并发编程、Java IO 和 NIO、JUnit单元测试

《零散知识点总结3》
该文章涉及 :Java Web、sprig的全家桶(spring、SpringMVC、springboot、springcloud)、微服务

《零散知识点总结4》
该文章涉及:JVM和GC、Linux、Git、RabbitMQ

《零散知识点总结5》
该文章涉及:多线程、反射、对象拷贝、异常、网络、容器

零散知识点总结5

多线程 (关于线程和进程的详细整合)

并发和并行的区别?

  • 并发:多个任务在同一个CPU 核上,按细分的时间片轮流(或交替)执行,逻辑上看来那些任务是同时执行的
    【两个队列和一台咖啡机】
  • 并行:多个处理器或多核处理器同时处理多个任务
    【两个队列和两台咖啡机】
    在这里插入图片描述

线程和进程的区别?

一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
在这里插入图片描述

什么是守护线程?怎么区分守护线程和其他线程? 守护进程和守护线程的区别?

守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。

怎么区分守护线程和其他线程?守护线程和普通线程的区别
【在JVM关闭之后执行的线程可以称是守护线程】

守护进程和守护线程的区别

推荐:https://www.cnblogs.com/lzlllll/p/10732127.html
无论是进程还是线程, 都遵循: 守护xxx 会等待主xxx 运行完毕后被销毁
守护进程 :只会守护到主进程的代码结束
守护线程 :会守护所有其他非守护线程的结束

运行完毕井非终止运行, 对主进程来说, 运行完毕指的是主进程代码运行完毕;对主线程来说, 运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕, 主线程才算运行完毕

主进程和子进程互不干扰,主进程执行完毕之后程序不会结束,会等待所有的子进程结束之后才结束
守护进程:是一个子进程,守护的是主进程
守护进程结束条件:主进程的代码结束,守护进程也结束

为什么主进程要等待子进程结束之后才结束?
因为主进程要负责给子进程回收一些系统资源

守护线程:

  1. 主线程会等待子线程的结束而结束
  2. 守护线程会随着主线程的结束而结束
    守护线程会守护主线程和所有的子线程

守护线程问题:

  1. 主线程需不需要回收子线程的资源?
    不需要,线程资源属于进程,所以进程结束了,线程的资源自然就被回收了
  2. 主线程为什么要等待子线程结束之后才结束?
    主线程结束意味着进程结束,所有的子线程都会结束
    要想让子线程能够顺利执行完,主线程只能等
  3. 守护线程到底是怎么结束的?
    主线程结束了,主进程也结束,守护线程被主进程的结束结束掉了

子线程结束、主线程结束、主进程执行完毕,守护进程(也是子进程)执行完毕, 主进程要回收守护进程(子进程)的资源,进程结束,守护线程被主进程的结束结束掉了(守护线程被主进程回收)

创建线程有哪几种方式?(3种)

  • 继承 Thread 重写 run 方法;
  • 实现 Runnable 接口;
  • 实现 Callable 接口。

创建线程池有哪几种方式?(7种)

线程池创建有七种方式,最核心的是最后一种:

  • newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;

  • newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;

  • newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;

  • newSingleThreadScheduledExecutor():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;

  • newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()类似,创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;

  • newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序;

  • ThreadPoolExecutor():是最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装。

说一下 runnable 和 callable 有什么区别?

runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充。

线程池中 submit() 和 execute() 方法有什么区别?

  • execute():只能执行 Runnable 类型的任务。
  • submit():可以执行 Runnable 和 Callable 类型的任务。
    Callable 类型的任务可以获取执行的返回值,而 Runnable 执行无返回值。

线程有哪些状态?

线程的状态:

  • NEW (new)尚未启动
  • RUNNABLE (runnable )正在执行中
  • BLOCKED(blocked) 阻塞的(被同步锁或者IO锁阻塞)
  • WAITING(waiting) 永久等待状态
  • TIMED_WAITING(timed_waiting) 等待指定的时间重新被唤醒的状态
  • TERMINATED (terminated)执行完成

线程池都有哪些状态?

  • RUNNING:(running)这是最正常的状态,接受新的任务,处理等待队列中的任务。
  • SHUTDOWN:(shutdown)不接受新的任务提交,但是会继续处理等待队列中的任务。
  • STOP:(stop)不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
  • TIDYING:(tidying)所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。
  • TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。

sleep( ) 和 wait( n)、wait( ) 的区别? notify()和 notifyAll()有什么区别?

  • sleep 方法: 是 Thread 类的静态方法,当前线程将睡眠 n 毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进行可运行状态,等待 CPU 的到来。睡眠不释放锁(如果有的话);
  • wait 方法: 是 Object 的方法,必须与 synchronized (同步)关键字一起使用,线程进入阻塞状态,当 notify 或者 notifyall 被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,释放互斥锁。
    【 notify:解除一个wait状态的线程;notifyall:解除所有处在wait状态的线程 】

notify()和 notifyAll()有什么区别?

notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。
notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。

线程的 run() 和 start() 有什么区别?

start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。

线程池中submit()和execute()方法有什么区别?

  • execute():只能执行 Runnable 类型的任务。
  • submit():可以执行 Runnable 和 Callable 类型的任务。

Callable 类型的任务可以获取执行的返回值,而 Runnable 执行无返回值。

在 Java 程序中怎么保证多线程的运行安全?

方法一:使用安全类,比如 Java. util. concurrent 下的类。
方法二:使用自动锁 synchronized。
方法三:使用手动锁 Lock。

手动锁 Java 示例代码如下:

Lock l= new ReentrantLock();
l. lock();
try {
    System. out. println("获得锁");
} catch (Exception e) {
    // TODO: handle exception
} finally {
    System. out. println("释放锁");
    l. unlock();
}

多线程中 synchronized 锁升级的原理是什么?

synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。

锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。

什么是死锁?怎么防止死锁?

当线程A独占a 锁,线性B独占b 锁,然后互相都想要独占对方的锁的情况,就会导致线程阻塞的现象称为死锁。

避免方法:

尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
尽量使用 Java. util. concurrent 并发类代替自己手写锁。
尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
尽量减少同步的代码块。

ThreadLocal 是什么?有哪些使用场景?

ThreadLocal (线程局部变量)为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal 的经典使用场景是数据库连接和 session 管理等。

谈谈对 ThreadLocal 的理解?
ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问 (通过get和set方法) 时能保证各个线程的变量相对独立对于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程上下文。

ThreadLocal 的作用是:

  1. 提供线程内的局部变量,
  2. 不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,
  3. 减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度在多线程并发的场景下
    1、传递数据:可以通过ThreadLocal 在同一线程,不同组件之间传递公共变量
    2、线程隔离:每个线程的变量都是独立的,不会互相影响。
    得在Thread内部中添加set方法,否则还是会出现线程不隔离问题
    set :将变量绑定到当前线程中
    get:获取当前线程绑定的局部变量
    remove :移除当前线程绑定的局部变量

扩展延伸:ThreadLocal类和Synchronized关键字对于Thread()内部中不加set方法的,还可以在它内部中使用synchronized关键字(即加锁)来实现线程隔离的功能。但是并发性降低了。所以使用ThreadLocal 更为合适—>因为这样可以使程序拥有更高的并发性。

就是说实现线程隔离有两种方法:

  1. 通过ThreadLocal 类,必须添加set()方法,高并发性【推荐】
  2. 通过Synchronized关键字(加锁)实现,但是并发性降低了

在哪些场景下会使用到 ThreadLocal?

  1. 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random) SimpleDateFormat对象本身不是一个线程安全的对象

    简单说一下SimpleDateFormat进化之路:
    (1)并发情况下,每个线程都需要用到SimpleDateFormat,例如1000个任务就对应1000个SimpleDateFormat对象,占用资源多,创建、销毁的开销大;-----一个线程就创建一个SimpleDateFormat对象

    (2)所有线程公用一个SimpleDateFormat对象,把对象提取出来变成static静态变量,需要用就直接获取,但是线程不安全,出现了并发安全问题;
    解决:加synchronized锁,但是要排队,多个线程不能同时工作,整体效率大大减低;-----SimpleDateFormat对象提取出来变成static静态变量再加上synchronized锁

    (3)为了不浪费过多的内存,同时又想保证线程安全,就让每个线程都拥有自己的SimpleDateFormat对象来达到这个目的。使用ThreadLocal,帮每个线程生成它自己的SimpleDateFormat对象(这个对象独享),这样既高效的使用了内存,又同时保证了线程安全。-----使用ThreadLocal

  2. 每个线程内需要保存全局变量 (如在拦截器获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。
    (1)一个用户系统,请求进来的时候,线程负责执行,但是是依次执行,方法可能分布在不同的类中。假如每个类代表不同的业务,第一个类中用到用户信息,每个方法都要用到的话就得把user对象层层传递,导致代码非常冗余;-----线程依次执行方法,需要贯穿其他方法的类就得层层传递,导致代码冗余

    (2)使用Map,把用户信息put进去,需要用就从静态的UserMap里面get。当然,使用HashMap是不够的的,因为线程不安全。使用synchronized或者直接把HashMap 替换成 ConcurrentHashMap。但都会影响性能 即便是使用性能比较好的 ConcurrentHashMap,它也是包含少量的同步,或者是 cas 等过程。相比于完全没有同步,它依然是有性能损耗的----使用ConcurrentHashMap加synchronized,性能损耗

    (3)使用ThreadLocal ,在调用user方法的时候,就往里面存入了 user 对象,而在后面去调用的时候,直接从里面用 get 方法取出来就可以了。没有参数层层传递的过程,非常的优雅、方便。----使用ThreadLocal ,没有参数层层传递的过程,保证线程安全,每个线程独享user对象

总结:

  1. TreadLocal 用来保证每个线程独享的对象,为每个对象创建一个副本,每个线程都只能修改自己所拥有的副本,不会影响其他线程的副本。这样就让原本并发情况下,线程不安全的情况变成了线程安全的情况

  2. ThreadLocal 用于每个线程内需要福利保存信息的场景,供其它方法更方便获取该信息,每个线程获取到的信息都可能不一样的,前面执行的方法设置了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参。

synchronized 、volatile

synchronized 关键字(考察底层实现)?

进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。

含义:(monitor 机制)

Synchronized 是在加锁,加对象锁。对象锁是一种重量锁(monitor),synchronized 的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问 synchronized 区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。

该关键字是一个几种锁的封装。

说一下 synchronized 底层实现原理?

synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。
在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。
但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

volatile 关键字?

该关键字可以保证可见性不保证原子性。

功能

  • 主内存和工作内存,直接与主内存产生交互,进行读写操作,保证可见性;
  • 禁止 JVM 进行的指令重排序。

解析:关于指令重排序的问题,可以查阅 DCL 双检锁失效相关资料。

volatile 能使得一个非原子操作变成原子操作吗?

能。
一个典型的例子是在类中有一个 long 类型的成员变量。如果你知道该成员变量会被多个线程访问,如计数器、价格等,你最好是将其设置为 volatile。为什么?因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。

volatile 修饰符的有过什么实践?
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。
volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。

volatile、Lock、 ReentrantLock 分别说出它们和synchronized 的区别

synchronized 和 volatile 的区别是什么?

synchronizedvolatile
synchronized 是修饰类、方法、代码段volatile 是变量修饰符
synchronized 可以保证变量的修改可见性和原子性volatile 仅能实现变量的修改可见性,不能保证原子性
synchronized 可能会造成线程的阻塞volatile 不会造成线程的阻塞

synchronized 和 Lock 有什么区别?

synchronizedLock
synchronized 可以给类、方法、代码块加锁lock 只能给代码块加锁
synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁
synchronized不能知道是否获取成功锁通过 Lock 可以知道有没有成功获取锁

synchronized 和 ReentrantLock 区别是什么?

synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。
主要区别如下:

  • ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
  • ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
  • ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
  • volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。

说一下 atomic 的原理?

atomic (原子性)主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作,从而避免 synchronized (同步)的高开销,执行效率大为提升。

hreadLocal(线程局部变量)关键字?

当使用 ThreadLocal 维护变量时,其为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程对应的副本。
ThreadLocal 内部实现机制
每个线程内部都会维护一个类似 HashMap 的对象,称为 ThreadLocalMap,里边会包含若干了 Entry(K-V 键值对),相应的线程被称为这些 Entry 的属主线程;

Entry 的 Key 是一个 ThreadLocal 实例,Value 是一个线程特有对象。Entry 的作用即是:为其属主线程建立起一个 ThreadLocal 实例与一个线程特有对象之间的对应关系;
Entry 对 Key 的引用是弱引用;Entry 对 Value 的引用是强引用。

我们为什么要使用线程池?核心线程池内部实现了解吗?

减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

可以根据系统的承受能力,调整线程池中工作线程的数目,放置因为消耗过多的内存,而把服务器累趴下(每个线程大约需要 1 MB 内存,线程开的越多,消耗的内存也就越大,最后死机)

核心线程池内部实现了解吗?
对于核心的几个线程池,无论是 newFixedThreadPool() 方法,newSingleThreadExecutor() 还是 newCachedThreadPool() 方法,虽然看起来创建的线程有着完全不同的功能特点,但其实内部实现均使用了 ThreadPoolExecutor 实现,其实都只是 ThreadPoolExecutor 类的封装。

反射

什么是反射?

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制

什么是 Java 序列化?什么情况下需要序列化?

Java 序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。

以下情况需要使用 Java 序列化:

  • 想把内存中的对象状态保存到一个文件中或者数据库中时候;
  • 想用套接字在网络上传送对象的时候;
  • 想通过RMI(远程方法调用)传输对象的时候。

动态代理是什么?有哪些应用?

《什么是动态代理?两种常用的动态代理方式》

动态代理是运行时动态生成代理类。
动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。

怎么实现动态代理?

JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的。

创建代理对象的两个方法:
//JDK动态代理
Proxy.newProxyInstance(三个参数);

//CGLib动态代理
Enhancer.create(两个参数);

对象拷贝

为什么要使用克隆?

因为new出来的对象的属性都是初始化值的时候,当我们需要新的对象来保存当前对象的“状态”,new就不能达到我们的效果,这时候就需要用到克隆了,因为克隆的对象可能包含一些已经修改过的属性。

如何实现对象克隆?

  • 实现 Cloneable 接口并重写 Object 类中的 clone() 方法。
  • 实现 Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

深拷贝和浅拷贝区别

先了解基本类型和引⽤类型:

  • 基本类型:string,number,boolean,null,undefiend,symbol
    基本数据类型的特点:直接存储在栈(stack)中的数据

  • 引⽤类型:Function,Array,Object
    引⽤数据类型的特点:存储的是该对象在栈中引⽤,真实的数据存放在堆内存⾥

要了解深拷贝和浅拷贝之间的区别,首先要了解清楚基本概念,拷贝(Copy)即复制

  • 浅拷贝(浅克隆)【shallowCopy】:创建⼀个新对象,保存原始对象属性值精准拷贝。如果属性是基本类型,拷贝的是基本类型的值,如果属性是引⽤类型,拷贝的是内存地址,并不会占⽤新的内存,这种情况下如果其中⼀个对象改变了这个地址,会影响到另⼀个对象。浅拷贝只复制指向某个对象的指针,⽽不复制对象本⾝。新旧对象共享同⼀块内存,所以浅拷贝基本类型之前互不影响,引用类型其中一个对象改变了地址,就会影响另一个对象;。
    换句话简单概括就是:只是增加了一个指针指向已存在的内存地址

  • 深拷贝(深克隆)【deepCopy】:将⼀个对象从内存中完整的拷贝⼀份出来,从堆内存中开辟⼀个新的区域存放新对象,增加了内存,且修改新对象不会影响原对象。新对象与原对象不共享内存。【不同内存,所以改变新对象不会影响原对象,他们之前互不影响。】
    换句话简单概括就是:是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误

举例子:

//======对象赋值
let obj1 = { name: '张三', action: { say: 'hi'};
let obj2 = obj1;
obj2.name = '李四';
obj2.action.say = 'hello'
console.log('obj1',obj1) 
// obj1 { name: '李四', action: { say: 'hello'}
console.log('obj2',obj2) 
// obj2 { name: '李四', action: { say: 'hello'}

//=======================浅拷贝  【注:concat(),slice()也属于浅拷贝】=======================
	//浅拷贝1  【Object.assign():任意多个源对象⾃⾝的可枚举属性拷贝给⽬标对象,然后返回⽬标对象。】
	let obj1 = { name: '张三', action: { say: 'hi'};
	let obj2 = Object.assign({}, obj1);
	obj2.name = '李四';
	obj2.action.say = 'hello'
	console.log('obj1',obj1) // obj1 { name: '张三', action: { say: 'hello'}
	console.log('obj2',obj2) // obj2 { name: '李四', action: { say: 'hello'}
	
	//浅拷贝2   【展开运算符…】:展开运算符是一个 es6特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。
	let obj1 = { name: '张三', action: { say: 'hi'};
	let obj2 = {... obj1};
	obj2.name = '李四';
	obj2.action.say = 'hello'
	console.log('obj1',obj1) // obj1 { name: '张三', action: { say: 'hello'}
	console.log('obj2',obj2) // obj2 { name: '李四', action: { say: 'hello'}

	//浅拷贝3【函数库lodash 的 _.clone方法】
	var _=require('lodash');
	var obj2=_.clone(obj1);

	//浅拷贝4【Array.prototype.concat()let arr2 = arr1.concat() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝
	//浅拷贝4【 Array.prototype.slice()let arr2 = arr1.slice() // 返回新数组,但当数组中嵌套数组对象时为浅拷贝

//============================================深拷贝=======================================
	//深拷贝1  【JSON.parse(JSON.stringify())】 缺点:不能处理函数和正则
	let obj1 = { name: '张三', action: { say: 'hi'};
	let obj2 = JSON.parse(JSON.stringify(obj1));
	obj2.name = '李四';
	obj2.action.say = 'hello'
	console.log('obj1',obj1) // obj1 { name: '张三', action: { say: 'hi'}
	console.log('obj2',obj2) // obj2 { name: '李四', action: { say: 'hello'}
	
	//深拷贝2 【jQuery.extend()let obj1 = { name: '张三', action: { say: 'hi'};
	let obj2 = $.extend(true, {}, obj1); //$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝
	obj2.name = '李四';
	obj2.action.say = 'hello'
	console.log('obj1',obj1); // obj1 { name: '张三', action: { say: 'hi'}
	console.log('obj2',obj2) ;	// obj2 { name: '李四', action: { say: 'hello'}

	//深拷贝 【函数库lodash的_.cloneDeep⽅法】
	var _ = require(‘lodash’);
	var obj2 = _.cloneDeep(obj1);
	
	//深拷贝 【手写递归实现】 解决循环引用的问题

在这里插入图片描述

在这里插入图片描述

异常、网络和设计模式异常

throw 和 throws 的区别?

  • throw:是真实抛出一个异常。
  • throws:是声明可能会抛出一个异常。

final、finally、finalize 有什么区别?

  • final:是修饰符,如果修饰类,此类不能被继承;如果修饰方法和变量,则表示此方法和此变量不能在被改变,只能使用。
  • finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,finally 部分可以省略,但如果 finally 部分存在,则一定会执行 finally 里面的代码。
  • finalize: 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。

try-catch-finally 中哪个部分可以省略?

try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同时省略,也就是说有 try 的时候,必须后面跟一个 catch 或者 finally。

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。

有关finally语句块

结论:

1、不管有木有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

举例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

常见的异常类有哪些?

  • NullPointerException 空指针异常
  • ClassNotFoundException 指定类不存在
  • NumberFormatException 字符串转换为数字异常
  • IndexOutOfBoundsException 数组下标越界异常
  • ClassCastException 数据类型转换异常
  • FileNotFoundException 文件未找到异常
  • NoSuchMethodException 方法不存在异常
  • IOException IO 异常
  • SocketException Socket 异常

Java中异常的分类

Throwable ——>Error、Exception

  • Error:严重问题,例如内存溢出
  • Exception ——>运行时异常: RuntimeException;编译时异常:AWTError

网络

HTTP 响应码有哪些?分别代表什么含义?

200:成功,Web 服务器成功处理了客户端的请求。
301:永久重定向,当客户端请求一个网址的时候,Web 服务器会将当前请求重定向到另一个网址,搜索引擎会抓取重定向后网页的内容并且将旧的网址替换为重定向后的网址。
302:临时重定向,搜索引擎会抓取重定向后网页的内容而保留旧的网址,因为搜索引擎认为重定向后的网址是暂时的。
	301302 区别:301 对搜索引擎优化(SEO)更加有利;302 有被提示为网络拦截的风险。
400:客户端请求错误,多为参数不合法导致 Web 服务器验参失败。
404:未找到,Web 服务器找不到资源。
500:Web 服务器错误,服务器处理客户端请求的时候发生错误。
503:服务不可用,服务器停机。
504:网关超时。

Forward(转发) 和 Redirect (重定向)的区别?

区分Forward(转发)Redirect (重定向)
浏览器 URL 地址【foward url 不会发生改变】Forward 是服务器内部的重定向,服务器内部请求某个 servlet,然后获取响应的内容,浏览器的 URL 地址是不会变化的;【redirect url 会发生改变】Redirect 是客户端请求服务器,然后服务器给客户端返回了一个 302 状态码和新的 location,客户端重新发起 HTTP 请求,服务器给客户端响应 location 对应的 URL 地址,浏览器的 URL 地址发生了变化。
数据的共享【forward 可以共享 request 里的数据】Forward 是服务器内部的重定向,request 在整个重定向过程中是不变的,request 中的信息在 servlet 间是共享的。【redirect 不可以共享 request 里的数据】Redirect 发起了两次 HTTP 请求分别使用不同的request。
请求的次数Forward 只有一次请求(效率较高)Redirect 有两次请求(效率较低)

get和post请求有哪些区别?

比较getpost
用途get请求用来从服务器获取资源post请求用来向服务器提交数据
传参数限制传递参数有大小限制,get 请求传输的数据受到 URL 长度的限制,而 URL 长度是由浏览器决定的post 请求传输数据的大小理论上来说是没有限制
参数编码URL参数明文传输,不安全post 请求使用二进制数据多重编码传递参数,安全
缓存请求浏览器主动缓存(get 请求可以被浏览器缓存被收藏为标签;)不会 (post 请求不会被缓存也不能被收藏为标签)
表单的提交方式get 请求直接将表单数据以 name1=value1&name2=value2 的形式拼接到 URL 上(http://www.baidu.com/action?name1=value1&name2=value2),多个参数参数值需要用 & 连接起来并且用 ? 拼接到 action 后面;post 请求将表单数据放到请求头或者请求的消息体中。

说说 TCP 与 UDP 的区别,以及各自的优缺点

  1. TCP面向连接(如打电话要先拨号建立连接):UDP是无连接的,即发送数据之前不需要建立连接。
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。
  3. UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  5. TCP对系统资源要求较多,UDP对系统资源要求较少。

说一下 HTTP 和 HTTPS 的区别

  • 端口不同:HTTP和 HTTPS 的连接方式不同没用的端口也不一样,HTTP是80, HTTPS 用的是443
  • 消耗资源:和HTTP相比,HTTPS通信会因为加解密的处理消耗更多的CPU和内存资源。
  • 开销: HTTPS 通信需要证书,这类证书通常需要向认证机构申请或者付费购买。

说说HTTP、TCP、Socket 的关系是什么?

  • TCP/IP 代表传输控制协议/网际协议,指的是一系列协议族。
  • HTTP 本身就是一个协议,是从 Web 服务器传输超文本到本地浏览器的传送协议。
  • Socket 是 TCP/IP 网络的 API ,其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在Socket 接口后面。对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。
    综上所述:
  • 需要 IP 协议来连接网络
  • TCP 是一种允许我们安全传输数据的机制,使用 TCP 协议来传输数据的 HTTP 是 Web 服务器和客户端使用的特殊协议。
  • HTTP 基于 TCP 协议,所以可以使用 Socket 去建立一个 TCP 连接

说一下HTTP的长连接与短连接的区别

HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

区别长连接短连接
定义从HTTP/1.1起,默认使用长连接,用以保持连接特性。在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭。如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。在HTTP/1.0中默认使用短链接,也就是说,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。如果客户端访问的某个HTML或其他类型的Web资源,如 JavaScript文件、图像文件、 CSS 文件等。当浏览器每遇到这样一个Web资源,就会建立一个HTTP会话
操作过程长连接的操作步骤是:建立连接——数据传输…(保持连接)…数据传输——关闭连接短连接操作步骤是:建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接
长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间 。对于频繁请求资源的户来说,较适用长连接。不过这里存在一个问题,存活功能的探测周期太长,还有就是它只是探测TCP连接的存活,属于比较斯文的做法,遇到恶意的连接时,保活功能就不够使了。在长连接的应用场景下,client端一般不会主动关闭它们之间的连接,Client与server之间的连接如果一直不关闭的话,会存在一个问题,随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,如关闭一些长时间没有读写事件发生的连接,这样可 以避免一些恶意连接导致server端服务受损;如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,这样可以完全避免某个蛋疼的客户端连累后端服务。短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽

TCP 为什么要三次握手,两次不行吗?为什么?

举例子:看成是一次完整的对话

A:B先生,您吃了吗?(第一次会话)
B:刚吃过,你呢?(第二次会话)
A:也吃了,……(第三次会话)
  • TCP 客户端和服务端建立连接需要三次握手,首先服务端需要开启监听,等待客户端的连接请求,这个时候服务端处于“收听”状态;
  • 客户端向服务端发起连接,选择 seq=x 的初始序列号,此时客户端处于“同步已发送”的状态;
  • 服务端收到客户端的连接请求,同意连接并向客户端发送确认,确认号是 ack=x+1 表示客户端可以发送下一个数据包序号从 x+1 开始,同时选择 seq=y 的初始序列号,此时服务端处于“同步收到”状态;
  • 客户端收到服务端的确认后,向服务端发送确认信息,确认号是 ack=y+1 表示服务端可以发送下一个数据包序号从 y+1 开始,此时客户端处于“已建立连接”的状态;
  • 服务端收到客户端的确认后,也进入“已建立连接”的状态。从三次握手的过程可以看出如果只有两次握手,那么客户端的起始序列号可以确认,服务端的起始序列号将得不到确认
    在这里插入图片描述

OSI 的七层模型都有哪些?

OSI七层模型一般指开放系统互连参考模型 (Open System Interconnect 简称OSI)是国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)联合制定的开放系统互连参考模型,为开放式互连信息系统提供了一种功能结构的框架。
在这里插入图片描述

如何实现跨域?

当浏览器执行 JS 脚本的时候,会检测脚本要访问的协议、域名、端口号是不是和当前网址一致,如果不一致就是跨域。跨域是不允许的,这种限制叫做浏览器的同源策略,简单点的说法就是浏览器不允许一个源中加载脚本与其他源中的资源进行交互。那么如何实现跨域呢?【JSONP、CORS方式、代理方式】

  • 服务端运行跨域,设置cors等于*
  • 在单个接口使用注解,@CrossOrigin 运行跨域;
  • 使用 jsonp 跨域;
  1. JSONP 方式
    script、img、iframe、link、video、audio 等带有 src 属性的标签可以跨域请求和执行资源,JSONP 利用这一点“漏洞”实现跨域。
    JSONP(跨域调用技术)实现原理:jsonp:JSON with Padding,它是利用script标签的 src 连接可以访问不同源的特性,加载远程返回的“JS 函数”来执行的。

《JSONP跨域的原理解析及其实现介绍》

<script>
	 var scriptTag = document.createElement('script');
	 scriptTag.type = "text/javascript";
	 scriptTag.src = "http://10.10.0.101:8899/jsonp?callback=f";
	 document.head.appendChild(scriptTag);
</script>

再看下 jQuery 的写法

$.ajax({
	// 请求域名
	url:'http://10.10.0.101:8899/login',
	// 请求方式
	type:'GET',
	// 数据类型选择 jsonp
	dataType:'jsonp',
	// 回调方法名
	jsonpCallback:'callback',
});
// 回调方法
function callback(response) {
	 console.log(response);
}

JSONP 实现跨域很简单但是只支持 GET 请求方式。而且在服务器端接受到 JSONP 请求后需要设置请求头,添加 Access-Control-Allow-Origin 属性,属性值为 * ,表示允许所有域名访问,这样浏览器才会正常解析,否则会报 406 错误。

response.setHeader("Access-Control-Allow-Origin", "*");

JSONP 的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要 XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

  1. CORS 方式
    CORS(Cross-Origin Resource Sharing)即跨域资源共享,需要浏览器和服务器同时支持,这种请求方式分为简单请求和非简单请求。

当浏览器发出的 XMLHttpRequest 请求的请求方式是 POST 或者 GET,请求头中只包含 Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(application/x-wwwform-urlencoded、multipart/form-data、text/plain)时那么这个请求就是一个简单请求。

对于简单的请求,浏览器会在请求头中添加 Origin 属性,标明本次请求来自哪个源(协议 + 域名 +端口)。

GET
// 标明本次请求来自哪个源(协议+域名+端口)
Origin: http://127.0.0.1:8080
// IP
Host: 127.0.0.1:8080
// 长连接
Connection: keep-alive
Content-Type: text/plain

如果 Origin 标明的域名在服务器许可范围内,那么服务器就会给出响应:

// 该值上文提到过,表示允许浏览器指定的域名访问,要么为浏览器传入的 origin,要么为 * 表示所有域名都可以访问
Access-Control-Allow-Origin: http://127.0.0.1:8080
// 表示服务器是否同意浏览器发送 cookie
Access-Control-Allow-Credentials: true
// 指定 XMLHttpRequest#getResponseHeader() 方法可以获取到的字段
Access-Control-Expose-Headers: xxx
Content-Type: text/html; charset=utf-8

Access-Control-Allow-Credentials: true 表示服务器同意浏览器发送 cookie,另外浏览器也需要设置支持发送 cookie,否则就算服务器支持浏览器也不会发送。

var xhr = new XMLHttpRequest();
// 设置发送的请求是否带 cookie
xhr.withCredentials = true;
xhr.open('post', 'http://10.10.0.101:8899/login', true);
xhr.setRequestHeader('Content-Type', 'text/plain');

另外一种是非简单请求,请求方式是 PUT 或 DELETE,或者请求头中添加了 ContentType:application/json 属性和属性值的请求。

这种请求在浏览器正式发出 XMLHttpRequest 请求前会先发送一个预检 HTTP 请求,询问服务器当前网页的域名是否在服务器的许可名单之中,只有得到服务器的肯定后才会正式发出通信请求。

预检请求的头信息:

 // 预检请求的请求方式是 OPTIONS
 OPTIONS
 // 标明本次请求来自哪个源(协议+域名+端口)
 Origin: http://127.0.0.1:8080
 // 标明接下来的 CORS 请求要使用的请求方式
 Access-Control-Request-Method: PUT
 // 标明接下来的 CORS 请求要附加发送的头信息属性
 Access-Control-Request-Headers: X-Custom-Header
 // IP
 Host: 127.0.0.1:8080
 // 长连接
 Connection: keep-alive

如果服务器回应预检请求的响应头中没有任何 CORS 相关的头信息的话表示不支持跨域,如果允许跨域就会做出响应,响应头信息如下:

HTTP/1.1 200 OK
// 该值上文提到过,表示允许浏览器指定的域名访问,要么为浏览器传入的 origin,要么为 * 表示所
var xhr = new XMLHttpRequest();
// 设置发送的请求是否带 cookie
xhr.withCredentials = true;
xhr.open('post', 'http://10.10.0.101:8899/login', true);
xhr.setRequestHeader('Content-Type', 'text/plain');
有域名都可以访问
Access-Control-Allow-Origin:http://127.0.0.1:8080
// 服务器支持的所有跨域请求方式,为了防止浏览器发起多次预检请求把所有的请求方式返回给浏览器
Access-Control-Allow-Methods: GET, POST, PUT
// 服务器支持预检请求头信息中的 Access-Control-Request-Headers 属性值
Access-Control-Allow-Headers: X-Custom-Header
// 服务器同意浏览器发送 cookie
Access-Control-Allow-Credentials: true
// 指定预检请求的有效期是 20 天,期间不必再次发送另一个预检请求
Access-Control-Max-Age:1728000
Content-Type: text/html; charset=utf-8
Keep-Alive: timeout=2, max=100
// 长连接
Connection: Keep-Alive
Content-Type: text/plain

接着浏览器会像简单请求一样,发送一个 CORS 请求,请求头中一定包含 Origin 属性,服务器的响应头中也一定得包含 Access-Control-Allow-Origin 属性。

  1. 代理方式
    跨域限制是浏览器的同源策略导致的,使用 nginx 当做服务器访问别的服务的 HTTP 接口是不需要执行 JS 脚步不存在同源策略限制的,所以可以利用 Nginx 创建一个代理服务器,这个代理服务器的域名跟浏览器要访问的域名一致,然后通过这个代理服务器修改 cookie 中的域名为要访问的 HTTP接口的域名,通过反向代理实现跨域。

Nginx 的配置信息:

server {
	 # 代理服务器的端口
	 listen 88;
	 # 代理服务器的域名
	 server_name http://127.0.0.1;
	 
	 location / {
		 # 反向代理服务器的域名+端口
		 proxy_pass http://127.0.0.2:89;
		 # 修改cookie里域名
		 proxy_cookie_domain http://127.0.0.2 http://127.0.0.1;
		 index index.html index.htm;
		 # 设置当前代理服务器允许浏览器跨域
		 add_header Access-Control-Allow-Origin http://127.0.0.1;
		 # 设置当前代理服务器允许浏览器发送 cookie
		 add_header Access-Control-Allow-Credentials true;
	 }
 }

前端代码:

var xhr = new XMLHttpRequest();
// 设置浏览器允许发送 cookie
xhr.withCredentials = true;
// 访问 nginx 代理服务器
xhr.open('get', 'http://127.0.0.1:88', true);
xhr.send();

容器

Java容器有哪些?Collection 和 Collections 有什么区别?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:

  • Collection:List(ArrayList、LinkedList、Vector、Stack)Set( HashSet、LinkedHashSet、TreeSet)
  • Map:HashMap(LinkedHashMap)TreeMap、ConcurrentHashMap、Hashtable

Collection 和 Collections 有什么区别?

  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。

  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。

List、Set、Map 之间的区别是什么?

在这里插入图片描述

怎么给List排序?

①:使用 Collections.sort 默认正序,可以传第二个参数自定义排序
②:自定义bean实现 Comparable 接口的compareTo方法。
③: 实现Comparator接口自定义比较器

你都知道哪些常用的Map集合?

HashMap、HashTable、TreeMap、LinkedHashMap

HashMap 和 Hashtable 有什么区别?

HashtableHashMap(底层数据结构是数组 + 链表,JDK 1.8 之后是数组 + 链表 + 红黑树)
出生的版本不一样,Hashtable 出生于 Java 发布的第一版本 JDK 1.0HashMap 出生于 JDK1.2
都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)
Hashtable 继承DictionaryHashMap 继承的是 AbstractMap,并且 AbstractMap 也实现了 Map 接口
Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的,是线程安全的非线程安全的
Hashtable 的 key 和value 都不能为 null,这个可以从 Hashtable 源码中的 put 方法看到,判断如果 value 为 null 就直接抛出空指针异常,在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况,那说明,这时候如果 key 为空,照样会抛出空指针异常HashMap 允许 key 和 value 为 null。在计算 hash 值的时候,有判断,如果key==null ,则其 hash=0 ;至于 value 是否为 null,根本没有判断过
Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低HashMap 为了提高计算效率,将哈希表的大小固定为了 2 的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多
Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了Enumeration 的方式HashMap 使用了 Iterator
默认情况下,初始容量不同,Hashtable 的初始长度是 11,之后每次扩充容量变为之前的2n+1(n 为上一次的长度)HashMap 的初始长度为 16,之后每次扩充变为原来的两倍(2n)
Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,如果需要多线程使用则用 ConcurrentHashMap 替代。推荐在单线程环境下使用 HashMap 替代

Hashtable 是线程安全,推荐使用 HashMap 代替 Hashtable;如果需要线程安全高并发的话,推荐使用 ConcurrentHashMap 代替 Hashtable。

讲讲ConcurrentHashMap 数据结构以及底层原理
在这里插入图片描述
在这里插入图片描述

如何决定使用 HashMap 还是 TreeMap?

在进行插入和删除操作是建议使用HashMap ,因为HashMap 插入快;在对一个key进行有序的遍历时,TreeMap会更好。

说一下 HashMap 的实现原理?

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。

说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

Array 和 ArrayList 有何区别?

ArrayArrayList
存储可以存储基本的数据类型和对象只能存储对象
扩容固定大小自动扩容
内置方法多,如addAll、removeAll、iteration 等方法只有 ArrayList 有

ArrayList 和 Vector (向量)的区别是什么?

ArrayListVector
线程安全非线程安全的线程安全的(Vector 使用了 Synchronized 来实现线程同步)
性能好一点差一点
扩容根据实际扩容,每次增加50%根据实际扩容,每次增加1倍

ArrayList和LinkedList的区别?分别用在什么场景?

区别:

ArrayListLinkedList
数据结构实现:Array(动态数组)的数据结构 , ArrayList 是数组队列,相当于动态数组数据结构实现:Link(链表)的数据结构,LinkedList 为双向链表结构,也可当作堆栈、队列、双端队列
随机访问效率:随机访问List时(get和set操作),ArrayList的效率更高随机访问效率: 随机访问List时,效率低是因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找
数据进行增加和删除的操作时,效率更低低是因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动数据进行增加和删除的操作时,LinkedList效率更高
从利用效率来看,ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用从利用效率来看,LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用
ArrayList主要控件开销在于需要在lList列表预留一定空间LinkList主要控件开销在于需要存储结点信息以及结点指针信息

共同点:都是对 List 接口的实现;
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

ArrayList和LinkedList的使用场景:

  • LinkedList:链表,插入删除快,查找修改慢。 适用于频繁增删的场景。
  • ArrayList:数组,查找快,插入删除慢。 适用于频繁查找和修改的场景。

如何实现数组和 List 之间的转换?

  • 数组转 List:使用 Arrays. asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。
    案例:
// list to array
List<String> list = new ArrayList<String>();
list. add("王磊");
list. add("的博客");
list. toArray();
// array to list
String[] array = new String[]{"王磊","的博客"};
Arrays. asList(array);

在 Queue 中 poll()和 remove()有什么区别?

  • 相同点:都是返回第一个元素,并在队列中删除返回的对象。
  • 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。
    案例:
Queue<String> queue = new LinkedList<String>();
queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());

哪些集合类是线程安全的?

Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。

迭代器 Iterator 是什么?

Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。

Iterator 怎么使用?有什么特点?

Iterator 使用代码如下:

List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
  String obj = it. next();
  System. out. println(obj);
}

Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

Iterator 和 ListIterator 有什么区别?

  • Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
  • Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
  • ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

怎么确保一个集合不能被修改?

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
示例代码如下:

List<String> list = new ArrayList<>();
list. add("x");
Collection<String> clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 运行时此行报错
System. out. println(list. size());

Ajax

Ajax 即Asynchronous Javascript And XML,翻译成中文就是**异步JavaScript和XML,Ajax默认是异步提交数据,可以实现前后端的数交互。
Ajax最大的优点

  1. Ajax最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。 (这一特点给用户的感受是在不知不觉中完成请求和响应过程)

  2. Ajax不需要任何浏览器插件,但需要用户允许javaScript在浏览器上执行。

     同步交互: 客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求。
     异步交互: 客户端发出一个请求后,无需要等待服务器响应结束,就可以发出第二个请求。
    

Ajax用于"局部刷新页面"和"异步提交"的特点 异步提交: 提交完认为不需要原地等待,立马就做其他事 局部刷新: 不在局限于整个页面的刷新,而是在于局部的某一个页面的小块刷新 因此和使用Form表单和后端进行数据交互的方式比较,具有以下优点:

Ajax使用JavaScript技术向服务器发送异步请求
Ajax请求无须刷新整个页面
因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以Ajax性能高!

Ajax工作原理

  1. Ajax相当于在用户和服务器之间加了一个中间层(Ajax引擎),使用户操作与服务器响应异步化。
  2. 并不是所有用户请求都是提交给服务器,像一些数据验证和数据处理等都交给Ajax引擎自己来做,只有确定需要从服务器读取数据时再由Ajax引擎代为向服务器提交请求。
  3. 客户端发送请求,请求交给Ajax,Ajax把请求提交给服务器,服务器进行业务处理,服务器响应数据交给Ajax对象,Ajax对象接收数据,由JavaScript把数据写到页面上。
    在这里插入图片描述

红黑树有哪几个特征?

在这里插入图片描述

原型模式-(clone,浅克隆与深克隆)

类加载

在这里插入图片描述

说Java虚拟机的生命周期及体系结构

Java虚拟机生命周期和体系结构

Java 是如何实现跨平台的?

注意:跨平台的是 Java 程序,而不是 JVM。JVM 是用 C/C++
开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的 JVM

答:我们编写的 Java 源码,编译后会生成一种 .class 文件,称为字节码文件。Java 虚拟机(JVM)就是负责将字节码文件翻译成特定平台下的机器码然后运行,也就是说,只要在不同平台上安装对应的 JVM,就可以运行字节码文件,运行我们编写的 Java 程序。

而这个过程,我们编写的 Java 程序没有做任何改变,仅仅是通过 JVM 这一 “中间层” ,就能在不同平台上运行,真正实现了 “一次编译,到处运行” 的目的。

equal 和 hashcode 作用和区别?

equalhashcode
作用:在 java里面比较两个对象是否相等一致在 java里面比较两个对象是否相等一致
区别1(性能方面)性能低,因为重写的equals()里一般比较的较为全面和复杂(它会对这个对象内所以成员变量一一进行比较),这样效率很低性能高,因为hashCode()对比,则只要生成一个hash值就能比较了,效率很高
区别2(可靠性方面)可靠的不可靠的,因为非常有可能的情况是,两个完全不同的对象的hash值却是一样的

由此得出结论:

  • equals()相等的两个对象他们的hashCode()肯定相等,即equals()绝对可靠
  • hahsCode()相同的两个对象,它们的equals()不一定相同。即用hashCode()比较相同的时候不靠谱
  • hashCode()不同的两个对象,他们的那么equals()肯定不同。即用hashCode()比较不同的时候肯定靠谱

有上面得出的结论,我们在使用equal和hashcode就需要注意以下几点:

  1. 对于大量数据的对比处理:先用hashcode()去对比,不同的就不需要用equals()去比较了,相同的就用equals()去比较,如果对比结果相同,那么这两个数字就是相同的,这样处理效率高准确性也高
  2. 平时用的集合框架中的hashMap、hashSet、hashTable 中对key的比较就是使用上述这种方法
  3. 而Obejct默认的equals和HashCode方法返回的是对象的地址相关信息。所以当我们通过new关键字创建了两个内容相同的对象,虽然他们的内容相同,但是他们在内存中分配的地址不同,导致它们的hashCode()不同,这肯定不是我们想要的。所以当我们要将某个类应用到集合中去的时候,就必须重写equals()方法和hashCode()方法。

案例:两个对象的 hashCode() 相同,用 equals()进行比较

String str1 = "通话";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d",  str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));

执行结果:

str1:1179395 | str2:1179395

false

解析:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。

扩展

1.阿里巴巴开发规约明确规定:

  • 只要重写了equals()方法,就必须重写hashCode()方法
  • 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法
  • 如果对象定义为Map的健,那么就必须重写equals()方法和hashCode()方法
  • String重写了equals()方法和hashCode()方法,所以我们可以非常愉快的使用String对象作为key

2.是不是每个对象都要重写这两个方法,到底什么时候重写?

  • 事实上一般情况下,我们并不需要重写这两个方法,只有该类被应用到集合框架中去的时候,才应该重写。

3.我能不能只重写equals()方法,不重写hashCode()方法?

  • 如果重写了equals()方法,比如说基于对象的内容实现的,而保留了hashCode()的实现不改变,那么最终出现的情况很可能是两个对象明明是“相等的”,但是hashCode()却不一样,这不是想要的

4.为什么需要hashCode

  • 通过hashCode可以提高对比的性能

== 和 equals 的区别是什么?

对于基本类型和引用类型 == 的作用效果是不同的

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同;

equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

如何保证单机保证3w tps 访问量?

一方面从请求入手,合并请求或者减少请求次数(不推荐);另一方面从异步入手,用MQ进行削峰处理(推荐——直接把请求发送到MQ中去,不处理业务。然后创建消费者去消费这些消息)
https://blog.csdn.net/qq_43645280/article/details/121438864

讲一下模版模式和策略模式的区别?

模板方法模式的主要思想:定义一个算法流程,将一些特定步骤的具体实现、延迟到子类。使得可以在不改变算法流程的情况下,通过不同的子类、来实现“定制”流程中的特定的步骤。

策略模式的主要思想:使不同的算法可以被相互替换,而不影响客户端的使用。

JDK、JRE有什么区别?

下面图解得很详细了
JDK:Java程序可以编译可以运行
JRE:Java程序可以运行
在这里插入图片描述

String属于基础数据类型吗?

不属于,string属于对象

8种基础数据类型所占字节数的大小(单位Byte)
整数型数据 byte 1 short 2 int 4 long 8
小数型数据 float 4 double 8
字符型数据 char 2
布尔型数据 boolean 1

Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:

  • String:String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,(经常改变字符串内容的情况下最好不要使用 String)
  • StringBuffer:声明的是可变的对象,可以在原有对象的基础上进行操作。StringBuffer 是线程安全的,所以在多线程环境下推荐使用 StringBuffer
  • StringBuilder:声明的是可变的对象,可以在原有对象的基础上进行操作。 StringBuilder 是非线程安全的,所以单线程环境下推荐使用 StringBuilder

String str="i"与 String str=new String(“i”)一样吗?

不一样,因为内存的分配方式不一样。
String str="i"的方式,Java 虚拟机会将其分配到常量池中;
String str=new String("i") 则会被分到堆内存中。

如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代码:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba

String 类的常用方法都有那些?

  • indexOf():返回指定字符的索引。
    -charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • getBytes():返回字符串的 byte 类型数组。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • substring():截取字符串。
  • equals():字符串比较。

抽象类

  1. 抽象方法:
    (1)用abstract修饰的方法,没有方法体,只有声明。
    (2)抽象方法必须用public或者protected,缺省情况下默认为public;
  2. 抽象类:
    (1)概念:包含了抽象方法的类就是抽象类。
    (2)特点:不能创建实例(抽象类不能直接实例化)

注意:

  • abstract 方法只能用访问修饰符 public 或者protected!
  • 有抽象方法的类只能定义成抽象类
  • 抽象类不能实例化
  • 抽象类可以包含属性、方法和构造器,但是构造器不能用new来实例化,只能用来被之类调用
  • 抽象类只能用来被继承
  • 抽象方法必须被之类实现
  • 抽象类不能用final声明,因为抽象类必须有子类,而final定义的类不能有子类
  • 子类对象实例化的时候,先执行父类的构造方法,再执行子类的构造方法

普通类和抽象类有哪些区别?

  • 普通类不能包含抽象方法,抽象类可以包含抽象方法。(换句话说就是,抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类)抽象类用**abstract /ˈæbstrækt/**修饰。
  • 抽象类不能直接实例化,普通类可以直接实例化。

抽象类可以使用final修饰吗?

不行,final修饰后,抽象类就不可以被继承了,定义抽象类的目的就是为了被继承

接口和抽象类的区别?

抽象类接口
对于子类的使用:extendsimplements
构造函数:可以有构造函数不可以有
实现数量:只能继承一个抽象类类可以实现很多个接口
访问修饰符可以是任意的访问修饰符接口的方法默认使用public修饰

Files的常用方法都有哪些?

  • Files. exists():检测文件路径是否存在。
  • Files. createFile():创建文件。
  • Files. createDirectory():创建文件夹。
  • Files. delete():删除一个文件或目录。
  • Files. copy():复制文件。
  • Files. move():移动文件。
  • Files. size():查看文件个数。
  • Files. read():读取文件。
  • Files. write():写入文件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习一个框架的知识框架可以按照以下步骤构建: 首先,你可以通过阅读官方文档来了解框架的架构和设计思想。这有助于你对框架的整体认知和理解。你可以尝试结合框架的不同部分来加深理解,并通过自己动手实践一些轮子来验证自己的理解是否正确。这种方式可以帮助你更好地理解框架的工作原理和使用方式。 其次,阅读框架的源代码是学习一个开源框架最强大的一步。通过仔细阅读源代码,你可以深入了解框架的实现细节和内部机制。这有助于你对框架的工作原理有更深入的理解。 最后,将你学到的知识整理成思维导图,并进行总结。你可以将零散知识点串联起来,形成一个完整的网络。这有助于你更好地理解和记忆所学的知识。你还可以将你的学习成果写成文章并发布出去,以便与他人分享和交流。 通过以上步骤,你可以逐步构建起一个完整的知识框架,更好地理解和应用所学的框架。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [如何学习一个框架](https://blog.csdn.net/mucaoyx/article/details/119123681)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值