Everybody wants go to heaven, but nobody wants to die.

  • 标★号为重要知识点

★有一个组合索引(A,B,C),可以有哪几种查询方式?

有A,AB,ABC三种查询方式。如果是AC的话,数据库会先利用索引查找到A索引的所有节点,接下来查找C节点时,
则没有使用索引。但并非使用了AC的查询就不走索引。但是如果是BC的话,则不走索引,因为在数据库的B树当中
是以A节点开始索引的。

★索引失效的原因有哪些?如何优化避免索引失效?

可以从索引的结构进行分析

  1. 索引树不存null值,不走索引
  2. 值变化太少的列,可能不走这个值的索引,或者直接走全表
  3. 索引树按照索引字符串是从首字母开始查找,所以前模糊查找不生效(类似多列索引,查询字段用后面的字段造成不走索引)
  4. 不确定的符号比较(无法在树中定位元素),比如<>、not in、not exist 之类的无法缩小查找范围的搜索。
  5. mysql自己估计全表扫描效率更高就不走索引了。
  6. 类型不匹配也会导致索引失效
  7. 使用函数

所以判断为何索引失效主要要从索引的结构去分析。大部分不走索引的情况都是因为条件是发散的,而不是收敛的。

数据库水平切分,垂直切分?

水平切分指的是拆分一张表中的行。
垂直切分指的是拆分一张表中的列。

2020-03-08 4 评论 46 浏览
阅读全文

  • 标★号为重要知识点

id全局唯一且自增,如何实现?

  • Redis的 incr 和 increby 自增原子命令
  • 统一数据库的id发放
  • 美团Leaf Leaf——美团点评分布式ID生成系统(批发号段)
  • Twitter的snowflake算法
  • UUID

★如何设计算法压缩一段URL?

通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是http://xx.xx/0 第二个是 http://xx.xx/1 第11个是 http://xx.xx/a 第依次往后,相当于实现了一个62进制的自增字段即可。

常用的url压缩算法是短地址映射法。具体步骤是:

  1. 将长网址用md5算法生成32位签名串,分为4段,,每段8个字符;
  2. 对这4段循环处理,取每段的8个字符, 将他看成16进制字符串与0x3fffffff(30位1)的位与操作,超过30位的忽略处理;
  3. 将每段得到的这30位又分成6段,每5位的数字作为字母表的索引取得特定字符,依次进行获得6位字符串;
  4. 这样一个md5字符串可以获得4个6位串,取里面的任意一个就可作为这个长url的短url地址。

★Dubbo负载均衡策略?

随机、轮询、最少使用、一致性哈希(除了一致性哈希外,都有加权)

2020-03-12 0 评论 40 浏览
阅读全文

  • 标★号为重要知识点

★JVM回收算法和回收器,CMS采用哪种回收算法,怎么解决内存碎片问题?

  CMD采用的是标记-清除算法。会导致内存碎片。
  可打开-XX:+UseCMSCompactAtFullCollection开关参数(默认打开)在进行Full GC之前整理内存碎片(称为“压缩”);
  使用-XX:CMSFullGCsBeforeCompaction参数(默认0)设置多少次不带压缩的Full CG之后才进行一次带压缩的Full GC。
  内存整理无法并行,还需要STW,需要适当调整内存整理的频率,在GC性能与空间利用率之间平衡。

★哪些情况会导致Full GC?

老年代满、永久代满、CMS回收失败、从新生代要放入老年代的对象超过剩余空间。

★JVM内存区域如何划分?

  • 内存区域只是一个划分规范,并不是所有虚拟机都是按照这样做的

  • 最新的java8内存模型为:程序计数器、本地方法栈、java虚拟机栈、堆。以及放置在本地内存的元数据区。元数据区即java7的永久代。

  • 堆:Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的),是Java垃圾收集器管理的主要区域。堆是被所有线程共享的,在JVM中只有一个堆。

  • 虚拟机栈:虚拟机栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表、操作数栈、指向当前方法所属的类的运行时常量池的引用、方法返回地址和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。

  • 本地方法栈:本地方法栈则是为执行本地方法(Native Method)服务的,在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一

  • 方法区:方法区与堆一样,是被线程共享的区域。方法区存储了类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。当方法区无法满足内存分配需求时,则抛出OutOfMemoryError异常。在HotSpot虚拟机中,用永久代来实现方法区,将GC分代收集扩展至方法区,但是这样容易遇到内存溢出的问题。JDK1.7中,已经把放在永久代的字符串常量池移到堆中。JDK1.8撤销永久代,引入元空间。

  • 程序计数器(线程私有):是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是Native方法,则为空。

  • 直接内存:在JDK1.4中新加入的NOI类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

★介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明

  • 程序计数器:不会出现内存溢出
  • 线程栈:每个线程栈需要一定的内存空间,虚拟机内存极小的情况下可能出现内存溢出。大部分情况出现stackOverFlow的异常。
  • 本地方法栈:一样有可能出现内存溢出。
  • 方法区:类信息或者静态变量过多的话会导致溢出。
  • 常量池:字符串过多的情况下溢出。
  • 直接内存区:

2020-03-09 0 评论 33 浏览
阅读全文

  • 标★号为重要知识点

说一下IOC和AOP?

IOC是依赖反转,通过依赖注入DI实现IOC。主要思想是面向抽象编程而不是面向具体实现编程。反转的意思是从依赖具体
实现变为依赖抽象。

AOP是面向切面编程,通过代理对代码无侵入的添加功能,实现切面编程。比如事务、日志等模块。
代理机制主要有jdk提供的动态代理和cglib提供的字节码动态代码。

★Spring中Bean的生命周期。

  1. 首先这个bean在进行开始实例化的时候会先进行调用该类的构造函数,默认是单例的
  2. 然后去注入属性中的bean
  3. 如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法;
  4. 如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来;使bean获得访问Spring容器的能力。
  5. 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;使得bean获取访问对应上下文的能力。
  6. 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization方法将被调用;如AutowiredAnnotationBeanPostProcessor
  7. 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
  8. 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization接口方法将被调用;
  9. 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
  10. 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;

Spring中注解Autowired和Resource的区别?

共同点:

两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

不同点 :

(1)@Autowired 为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;
只按照byType注入。 @Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,
可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。

2020-03-09 0 评论 42 浏览
阅读全文

  • 标★号为重要知识点

TCP协议在哪一层?IP协议在那一层?HTTP在哪一层?

从上到下依次是HTTP(应用层)、TCP(传输层)、IP(网络层)。

http协议:
解决web应用的传输规则。

tcp协议:
主要解决网络的可靠传输。

ip协议:
IP协议的任务是对数据报进行相应的寻址和路由选择,并从一个网络转发到另一个网络。
IP协议的另一项工作是分割和重编在传输层被分割的数据包。
IP协议提供尽最大努力投递(Best-effort Delivery)的传输服务,是不可靠无连接的数据报服务。

★讲一下TCP的连接和释放连接。

三次握手建立连接:

  1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,
    Client进入SYN_SENT状态,等待Server确认。

  2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1, ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

  3. 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1, 并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server 进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

四次挥手释放连接:

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号), Server进入CLOSE_WAIT状态。
  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,
    Server进入CLOSED状态,完成四次挥手。

★为什么要三次握手四次挥手?

为什么需要三次握手?

防止已失效的连接请求又传送到服务器端,因而产生错误。通俗的说法如果两次握手的话,就是A发送一个分组给B,结果在网络中滞留了。结果又发了一个分组给B,B收到分组,回复A。开始建立连接传送数据。传好了之后。之前在网络滞留的分组又到了B,结果又建立了连接。

比较严谨的解释是:为了实现可靠数据传输, TCP 协议的通信双方,都必须维护一个序列号,以标识发送出去的数据包中,哪些是已经被对方收到的。三次握手的过程即是通信双方相互告知序列号起始值,并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认。

为什么需要四次挥手?

因为TCP有个半关闭状态,假设A.B要释放连接,那么A发送一个释放连接报文给B,B收到后发送确认,这个时候A不发数据,但是B如果发数据A还是要接受,这叫半关闭。然后B还要发给A连接释放报文,然后A发确认,所以是4次。

2020-03-08 0 评论 27 浏览
阅读全文

  • 标★号为重要知识点

CentOS 和 Linux的关系?

CentOS是RedHat的一个分支,RedHat是Linux的一个发行版本,RedHat与CentOS的区别在于,
RedHat收费,CentOS免费

怎么杀死进程?

一般情况下,终止一个前台进程使用 Ctrl + C 就可以了。对于一个后台进程就须用 kill 命令来终止。
我们会先使用 ps、top 等命令获得进程的 PID,然后使用 kill 命令来杀掉该进程。

线程,进程区别?

进程是资源分配的最小单位,线程是CPU调度的最小单位。

系统线程数量上限是多少?

具体看内存,还有系统定义的线程数 /proc/sys/kernel/pid_max 32768

什么是页式存储?

连续存储管理不足:

  • 对空间要求高
  • 会形成很多碎片
  • 通过移动技术减少碎片会增加系统的开销

分页式存储管理的基本原理如下:

  • 页框:物理地址分成大小相等的许多区,每个区称为一块(又称页框 page frame);
  • 页面:逻辑地址分成大小相等的区, 区的大小与块的大小相等,每个区称一个页面(page)。
  • 逻辑地址形式:与此对应,分页存储器的逻辑地址由两部分组成:页号和单元号。
  • 用户进程在内存空间中的每个页框内的地址是连续的,但页框和页框之间的地址可以不连续
  • 页表和地址转换:在进行存储分配时,总是以块(页框)为单位进行分配,一个作业的信息有多少页,那么在把它
    装入主存时就给它分配多少块。但是,分配给作业的主存块是可以不连续的,即作业的信息可按页分散存放在主存
    的空闲块中,这就避免了为得到连续存储空间而进行的移动。

★操作系统里的内存碎片你怎么理解,有什么解决办法?

内存碎片通常分为内部碎片和外部碎片:

1. 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片,  
通常内部碎片难以完全避免;
2. 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用  
的内存区域。  
现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,  
使段内的页可以不必连续处于同一内存区域。

2020-03-07 0 评论 24 浏览
阅读全文

  • 标★号为重要知识点

★Map的分类和常见用法

java.util.Map接口有以下四种实现类

  • HashMap:
最常用的Map,根据键的hashcode值来存储数据,根据键可以直接获得他的值(因为相同的键hashcode值相同,
在地址为hashcode值的地方存储的就是值,所以根据键可以直接获得值),具有很快的访问速度,遍历时,
取得数据的顺序是随机的,HashMap最多只允许一条记录的键为null,允许多条记录的值为null,
HashMap不支持线程同步,即任意时刻可以有多个线程同时写HashMap,这样对导致数据不一致,如果需要同步,
可以使用synchronziedMap的方法使得HashMap具有同步的能力或者使用concurrentHashMap。
  • HashTable:
与HashMap类似,不同的是,它不允许记录的键或值为空,支持线程同步,即任意时刻只能有一个线程写HashTable,
因此也导致HashTable在写入时比较慢!
  • LinkedHashMap:
是HahsMap的一个子类,但它保持了记录的插入顺序,遍历时先得到的肯定是先插入的,也可以在构造时带参数,
主要是利用链表维护插入顺序,再使用哈希表维护哈希位置。按照应用次数排序,在遍历时会比HahsMap慢,不过有个例外,  
当HashMap的容量很大,实际数据少时,遍历起来会比LinkedHashMap慢(因为它是链啊),因为HashMap的遍历速度和它容量有关,  
LinkedHashMap遍历速度只与数据多少有关
  • TreeMap:
实现了sortMap接口,底层是红黑树。能够把保存的记录按照键排序(默认升序),也可以指定排序比较器,遍历时得到的数据是拍过序的。
  • 常见使用:
在Map中插入,删除,定位元素:HashMap
要按照自定义顺序或自然顺序遍历:TreeMap
要求输入顺序和输出顺序相同:LinkedHashMap
需要同步的话使用:HashTable(更推荐ConcurrentHashMap)

2020-03-06 0 评论 27 浏览
阅读全文

  • 标★号的知识点为重要知识点

java中IO流的体系?

Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四 个):InputStream,OutputStream,Reader,Writer。基于这四种IO流父类根据不同需求派生出其他IO流。

★BIO,NIO,AIO?

   BIO是同步阻塞IO,NIO是同步非阻塞IO,AIO是异步非阻塞IO;三种IO方式相比较而言,BIO是一个客户端对应一个线程,  
   优化的话可以用线程池进行线程复用,但本质还是一个客户端-服务端通信对应一个线程;NIO只需要一个线程负责多路复用  
   器selector的轮询,就可以处理不同客户端channel中的读/写事件,所以多个客户端实际只对应一个线程,另外服务器端  
   和客户端均使用缓冲区的方式进行读写;AIO不需要轮询去查看读写事件是否就绪,而是由内核通过回调函数通知并完成后续  
   操作。

NIO和IO的区别

第一点,NIO少了1次从内核空间到用户空间的拷贝。

ByteBuffer.allocateDirect()分配的内存使用的是本机内存而不是Java堆上的内存,和网络或者磁盘交互都在操作系统的内核空间中发生。
allocateDirect()的区别在于这块内存不由java堆管理, 但仍然在同一用户进程内。

第二点,NIO以块处理数据,IO以流处理数据

第三点,非阻塞,NIO1个线程可以管理多个输入输出通道

讲讲IO里面的常见类,字节流、字符流、接口、实现类

InputStream和OutStream属于字节流。底下有ByteArray、Object、File等实现类,以及Buffer增强。
Reader和Writer属于字符流。底下有String、File、Buffer等实现。

讲讲NIO。

NIO通过观测多个缓冲区,哪个缓冲区就绪的话,就处理哪个缓冲区。原本的IO会对一个缓冲区等待,效率比较慢,
而现在NIO是对一堆缓冲区进行等待,效率比较高。

2020-03-05 0 评论 33 浏览
阅读全文

  • 标★号的知识点为重要知识点

★为什么重写equals还要重写hashcode?

如果重写了equals的话,可以根据自己的规则进行判定两个对象是否相等。(比如学号姓名一样的学生实体即为相等)
但是,如果将实体存进与哈希有关的集合当中时,哈希地址是根据hashcode进行计算的。如果没有重写hashcode的时候,默认返回的是引用值。
这将会导致Hash表当中存入两个相同的学生。
所以所有有关hash的方法都会出问题。

两个对象值相同(x.equals(y) == true),但却可有不同的hashCode?

如果重写equals但未重写hashCode时的话会导致这样的问题。按照规范的话,两对象equals相同的话,hashCode也必须相同。

Object默认的hashcode实现?

Object类对于hashcode并没有具体的实现,调用的是jvm底层的c++代码进行计算。该方法返回的是一个int数字,底层的cpp代码中
一共有六种默认的实现方式。

Object基类有哪些方法?

类中的方法:clone(),但是使用该方法必须实现Java.lang.Cloneable接口,
equal()方法判断引用是否一致,指向同一对象,即相等于==,只有覆写了equals()方法之后,
才可以说不同。hashcode(),对象的地址, toString(), finalize()。

==比较的是什么?

在java中==符号比较的是两个对象在内存当中的引用值。

若对一个类不重写,它的equals()方法是如何比较的?

若不重写equals的话,将直接调用基类Object的equals方法,该方法比较的还是内存当中的引用值。

★java中的四种引用类型:

  • 强引用:java默认声明的就是强引用,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。
  • 软引用:在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。
  • 弱引用:弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。(常用作集合的key,避免内存泄露)
  • 虚引用:虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收。

java的基础类型和字节大小。

一个字节是8位
整形数: byte 1字节 short 2字节 int 4字节 long 8字节
浮点数:float 4字节 double 8字节
char:2字节
boolean:1字节

Java支持的数据类型有哪些?什么是自动拆装箱?

  • char
  • byte
  • short
  • boolean
  • long
  • float
  • double
  • int
    自动拆装箱是在代码编译之后,自动进行 int i = Integer.valueOf(i) 的转换操作,
    最早的1.5JDK之前没有自动拆装箱,所以当时是需要手动写拆箱装箱。

什么是自动拆装箱?

Integer total = 99;这是自动装箱。int totalprim = total;这是自动拆箱。意思是将一个int基本类型放到一个需要
Integer类型的地方会自动装箱,将一个Integer类型的包装类型放到需要int的地方会自动拆箱。包装类型当中,有一个类似
字符串常量池的常数常量池,数值大小在-127~128之间。

int 和 Integer 有什么区别?

  • int是基本类型,Integer是int的包装类型。
  • int的默认值是0,Integer的默认值null。
  • Integer缓存了-128~127之间的数。

java8的新特性。

  • lambda表达式支持函数式编程,简化代码。
  • 接口可有默认方法与静态方法。
  • 方法引用的简便写法。
  • 可重复注解。
  • 扩展注解的使用范围:局部变量、泛型变量、异常。
  • 可获取参数的名字。
  • Optional对于Null优雅的处理。
  • java集合中的Stream的增强处理(类似数据库),以及并行处理。
  • Date/Time API更好的支持。(duration特别好用)
  • 内置javasrcipt引擎
  • Base64包
  • JVM中的永久区被移除,换做元数据区

lambda表达式的优缺点(待讨论)

  • 优点:
    • 简洁,不再需要匿名内部类。
    • 并行计算在大数据量的情况下会比for循环更块
  • 缺点:
    • 普通情况下比for循环慢
    • 调试不方便
    • 类型转换要特殊处理

一个十进制的数在内存中是怎么存的?

以二进制补码形式存储,最高位是符号位,正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,符号位为1,其他位取反

浮点数为什么精度会发生丢失?

2进制的小数无法精确的表达10进制小数,计算机在计算10进制小数的过程中要先转换为2进制进行计算,这个过程中出现了误差。
例如0.125 二进制表示为0.001 但0.4转换为二进制的话,计算会出现无限小数。所以会导致精度丢失。

★浅析Java中的final关键字?

修饰方法表示不能重写,修饰类型表示不能继承,修饰变量表示需要初始化并赋值,并且不能重新赋值。
(如果这个值是一个对象,对象内的数据可改变,但此对象的引用不能改变)。

什么是值传递和引用传递?

值传递代表的是将实参的值拷贝传给形参,形参的改变并不会影响到实参。
引用传递指的是将实参的引用值传给行参,方法内基于引用值找到对象并修改对象中的值是会生效的,但地址值依然是引用值的拷贝,无法生效。

Java中方法参数的传递规则?

java中是值传递的方式。意味着方法参数这个变量都不是真正要操作的变量,而是变量的拷贝。引用类型的地址值改变
也不会影响原来的变量,但修改引用类型中的值,可以生效,因为修改会直接作用到堆中的java对象。

★当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

值传递指的是传递进来的对象的引用值是一份拷贝。(比如将传递进方法的参数对象的引用值设为null的话,
在方法外部调用该对象的方法的话,是不会报空指针的。因为传递进来的是引用的拷贝值。)

String和StringBuffer的区别?

  • String不可变,进行更改字符串的话相当于改了字符串的引用。
  • StringBuffer可变,修改字符串是基于原来的字符串进行修改,不会创建新的字符串。

★StringBuffer和StringBuilder的区别,从源码角度分析?

StringBuffer源码中方法加了synchronized进行修饰,所以是线程同步的。而StringBuilder中的方法没有用
synchronized进行修饰,所以不是线程同步的。

String,Stringbuffer,StringBuilder的区别?

String是字符串常量,编译之后其实是调用StringBuilder。
StringBuilder是字符串变量,线程非安全,效率高。在循环拼接字符串中,推荐使用StringBuider.
StringBuffer是字符串变量,线程安全,效率低。

如何输出一个某种编码的字符串?

new String(str.getBytes("ISO-8859-1"), "GBK"); 通过添加字符编码进行转换。

★什么是字符串常量池?

在Java堆中,有一块专门放置字符串的池子不同JDK版本不一样,在编译期就存在的字符串将会
直接存入这个池中,或者在不同代码地方的相同的字符串将会直接引用同一个字符串,为什么能
这样引用是因为字符串的不可变性,java常量池的实现其实是享元模式的思想,可以节省创建的时间,并且节省空间。

★String方法intern()的作用是?

  1. 当常量池中不存在"abc"这个字符串的引用,将这个对象的引用加入常量池,返回这个对象的引用。
  2. 当常量池中存在"abc"这个字符串的引用,返回这个对象的引用;

★String为什么是不可变的?

因为常量池中的字符串会被多个地方引用到,如果字符串是可变的话,更改一个字符串将导致原先引用
该字符串的所有地方都被改变了。这个不可变是指堆中常量池的字符串本身不可变,代码中的引用是可变的。
String类本身设计出不可变是为了防止客户程序员继承String类,造成破坏。

为什么String不可变,为什么是final?

  • 因为String类中持有的value数组使用final进行修饰的,所以它的数组引用就变成不可变的了。
  • 这么做的原因之一是因为String有常量池这个概念。比如一个String a在常量池中被很多变量
  • 引用了,如果String是可变的,当改变了a当中的字符,会造成所有引用这个字符变量的地方都跟着改变。
  • 但如果String是不可变的话,a原本的字符不变,将一个新的字符串赋予给a,这样就不会影响到其他引用之前a字符串的地方。

String能继承吗?

  • 不能,因为String类是经过final修饰的。
  • 这么做的原因可能是因为String是底层的类,用final修饰的话,自然而然的方法也会被final修饰。因此在调用String的任何方法的时候,都采用JVM的内嵌机制,效率会有较大的提升。
  • 还有可能是出于安全的考虑,防止继承的子类改写String的方法,再将子类以String父类的形式进行调用。例如使用+号操作符重载?

★String s = new String("xyz");究竟产生了几个对象,从JVM角度谈谈?

当JVM执行到这一行的时候,会先去常量池中查看是否有xyz的字符串,没有的话,会新建这个字符串放入
常量池,如果有的话则不创建。new 操作符将会在对象堆中创建一个字符串对象。所以这个操作可能生成1
个或者2个对象。

★String拼接字符串效率低,你知道原因吗?

如果是“ab”+"cd"的话,并不会效率低,因为经过编译优化之后会自动变成“abcd”,
但如果是代码中动态拼接,或者循环拼接的话,比如a+b+c+d将会产生a,ab,abc这三个临时的字符串,  
创建不必要的字符串是浪费性能和空间的,所以大部分的String拼接字符串效率会低。
其实+号,编译器转换成StringBuilder的append。有点类似于C++当中的操作符重载。

String的常见API?

  • indexOf 检索子串的位置
  • substring 获取子串
  • trim 去除字符串前后空白
  • charAt 获取字符的位置
  • startWith\endWith 检测是否是指定字符串开头或者结尾
  • toUpperCase 转换成大写
  • toLowerCase 转换成小写
  • valueOf 将其他类型转为字符串
  • split 分隔字符串

String.valueOf和Integer.toString的区别?

没有区别。String.valueOf(i),方法内部调用的也是Integer.toString(i)。

★Java中的subString()真的会引起内存泄露么?

假设A字符串很大,B=A.substring,在java1.6版本以前B会持有A字符串类型内置的字符数组,  
AB是共享同一个字符数组,就算A被回收了,但是B还是持有A字符串内很大的字符数组,在Java1.7  
之后修复了这个问题,在substring中,会重新复制一份字符数组给B,性能会比1.6版本差一点,  
但就不会导致内存泄露。假设A="123456789",B="9",但是其实B当中还是持有完整的"123456789"数组

&和&&的区别?

  • 一个是按位与,一个是逻辑与。逻辑与会短路。按位与不会短路。

在Java中,如何跳出当前的多重嵌套循环?

  • 定义一个标号。然后在内层循环当中break 标号;
      ok:

      for (int j = 0; j < 1000; j++) {
          for (int k = 0; k < 1000; k++) {
              System.out.println(k);
              break ok;
          }
      }

你能比较一下Java和JavaSciprt吗?

  • java是编译型语言,强类型,面向对象。由JVM执行。
  • javascript是解释性语言,弱类型,一般面向过程。由浏览器引擎执行。

简述正则表达式及其用途。

  • 主要用来匹配出特定规则的字符串。(如电话号码)

2020-03-01 2 评论 42 浏览
阅读全文

java中有哪些代理模式?

最原始的静态代理。 以及JDK和Cglib的动态代理。

IO流熟悉吗,用的什么设计模式?

装饰器模式、适配器模式

懒汉式的单例模式如何实现单例?

通过双重检测以及synchronized,volatile关键字实现。

介绍一下策略模式?

通过定义一个策略接口,将具体的算法逻辑实现交给子类实现,源代码只需要调用策略接口就行。

说说你所熟悉或听说过的j2ee中的几种常用模式?

IO流的装饰器模式,Web过滤器的责任链模式,Spring的单例模式和工厂模式,
Spring中根据不同配置方式进行初始化的策略模式

★简述一下你了解的Java设计模式(总结)

标星号的为常用设计模式

★单例模式:保证某个类只能有一个唯一实例,并提供一个全局的访问点。
★简单工厂:一个工厂类根据传入的参数决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建一组相关或依赖对象族,比如创建一组配套的汉堡可乐鸡翅。
★建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造,最后再build。
★原型模式:通过复制现有的实例来创建新的实例,减少创建对象成本(字段需要复杂计算或者创建成本高)。
 
★适配器模式:将一个类的方法接口转换成我们希望的另外一个接口。
★组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。(无限层级的知识点树)
★装饰模式:动态的给对象添加新的功能。
★代理模式:为对象提供一个代理以增强对象内的方法。
亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象(Integer中的少量缓存)。
★外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化(比如插座和充电器,他们之间相插是固定的,
但是至于插座是插在220V还是110V,充电器是充手机还是pad可以自主选择)。
 
★模板方法模式:定义一个算法步骤,每个小步骤由子类各自实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
★策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
★状态模式:允许一个对象根据其内部状态改变而改变它的行为。
★观察者模式:被观测的对象发生改变时通知它的所有观察者。
备忘录模式:保存一个对象的某个状态,以便在适当的时候恢复对象。
中介者模式:许多对象利用中介者来进行交互,将网状的对象关系变为星状的(最少知识原则)。
命令模式:将命令请求封装为一个对象,可用于操作的撤销或重做。
访问者模式:某种物体的使用方式是不一样的,将不同的使用方式交给访问者,而不是给这个物体。(例如对铜的使用,造币厂
造硬币。雕刻厂造铜像,不应该把造硬币和造铜像的功能交给铜自己实现,这样才能解耦)
★责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,
并且沿着这条链传递请求,直到有对象处理它为止。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

2020-03-06 0 评论 39 浏览
阅读全文

  • 标★号的知识点为重要知识点

介绍一下Syncronized锁,如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么?

- Synchronized修饰成员方法的话锁住的是实例对象。修饰静态方法的话,锁住的是类对象。
(可以类比数据库中的表锁和行锁的机制)

★请你介绍一下volatile?

- 使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效
- (非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,
- 线程B读取这个变量的值时可能读到的并不是是线程A更新后的值)。
- volatile会禁止指令重排 volatile具有可见性、有序性,不具备原子性。 注意,volatile不具备原子性。(不能保证并发累加问题)

★Synchronized和Lock的区别?

  • synchronized :
    • 是一个Java的关键字,JVM隐式锁。
    • 会自动释放锁。
    • 去获得锁的时候,会阻塞。
    • 不可获取锁的状态。
    • 可重入,不可中断,非公平锁
    • 性能没有Lock好。(但是随着JDK版本更迭,其实synchronized的性能也不错)
  • Lock:
    • 是java的一个类。
    • 需要手动释放锁。一般在finally中释放。(避免异常的时候没有释放锁)
    • 线程可以尝试获得锁,线程可以不用一直阻塞等待。
    • 可以获取锁的状态,通过trylock()方法。
    • 可重入,可中断,可公平。
    • 性能良好,适合大量并发。

.概括的解释下线程的生命周期周期状态。

1.新建状态(New):new Thread(runable); new操作完成后,此时线程刚被创建是新建状态。
2.就绪状态(Runnable): 当调用thread.start()方法被调用的时候,线程进入就绪状态,可以开始抢占cpu
3.运行状态(Running): 当线程分配到了时间片之后,开始进入运行状态。
4.阻塞状态(Blocked): 当线程还未执行结束前,让出cpu时间片(主动或者被动的),线程进入阻塞状态
5.死亡状态(Dead):线程正常运行结束或者遇到一个未捕获的异常,线程进入死亡状态。

2020-03-05 0 评论 36 浏览
阅读全文

自律。
早起。
坚信会看到自己成功的那一刻。
像渴望呼吸那样渴望成功。
永不放弃自己的梦想。
Everybody wants go to heaven, but nobody wants to die.
成功是需要代价的。
                   - 2020.3.4

2020-03-04 0 评论 43 浏览
阅读全文

整理自《编写高质量代码:改善Java程序的151条建议》

一、Java开发中通用的方法和准则

  • 不要在常量和变量中出现易混淆的字母;
  • 莫让常量蜕变成变量;
  • 三元操作符的类型务必一致;
  • 避免带有变长参数的方法重载;
  • 别让null值和空值威胁到变长方法;
  • 覆写变长方法也要循规蹈矩;
  • 警惕自增的陷阱;
  • 不要让旧语法困扰你;
  • 少用静态导入;
  • 不要在本类中覆盖静态导入的变量和方法;
  • 养成良好习惯,显式声明UID;
  • 避免用序列化类在构造函数中为不变量赋值
  • 避免为final变量复杂赋值;
  • 使用序列化类的私有方法巧妙解决部分属性持久化问题;
  • break万万不可忘;
  • 易变业务使用脚本语言编写;
  • 慎用动态编译;
  • 避免instantceof非预期结果;
  • 断言对决不是鸡肋;
  • 不要只替换一个类;

二、基本类型

  • 使用偶判断,不用奇判断;
  • 用整数类型处理货币;
  • 不要让类型默默转换;
  • 边界,边界,还是边界;
  • 不要让四舍五入亏了一方;
  • 提防包装类型的null值;
  • 谨慎包装类型的大小比较;
  • 优先使用整型池;
  • 优先选择基本类型;
  • 不要随便设置随机种子;

三、类、对象及方法

  • 在接口中不要存在实现代码;
  • 静态变量一定要先声明后赋值;
  • 不要覆写静态方法;
  • 构造函数尽量简化;
  • 避免在构造函数中初始化其他类;
  • 使用构造代码块精炼程序;
  • 使用静态内部类提供封装性;
  • 使用匿名类的构造函数;
  • 匿名类的构造函数很特殊;
  • 让多重继承成为现实;
  • 让工具类不可实例化;
  • 避免对象的浅拷贝;
  • 推荐使用序列化实现对象的拷贝;
  • 覆写equals方法时不要识别不出自己;
  • equals应该考虑null值情景;
  • 在equals中使用getClass进行类型判断;
  • 覆写equals方法必须覆写hashCode方法;
  • 推荐覆写toString方法;
  • 使用package-info类为包服务;
  • 不要主动进行垃圾回收;

2019-12-31 0 评论 194 浏览
阅读全文

  • 尽可能使用局部基本数据类型变量。
  • 及时关闭流。
  • 尽可能多使用三目运算符,代码看起来会比较清晰
  • 尽量减少对变量的重复计算。
  • 尽量采用懒加载的策略,即在需要的时候才创建。
    如果已知列表的长度,为底层以数组方式实现的集合、工具类指定初始长度。
  • 基于效率和类型检查的考虑,应该尽可能使用Array,无法确定数组大小时才使用ArrayList。
  • 当复制大量数据时,使用System.arraycopy()命令。
  • 乘法和除法可以使用移位操作进行优化。除非所需性能要求高,不然不必用移位来进行优化。例如我们平常的业务开发使用移位进行优化,反而使得代码的可读性降低。
  • 接口中的方法、属性尽量不要加任何访问修饰符。依据Alibaba的java代码规范。
  • 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
  • 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
  • 尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销。
  • 不要将数组声明为public static final。否则不管这个数组有没有使用大,JVM都会为这个数组保留内存空间。
  • 尽量在合适的场合使用单例。
  • 尽量避免随意使用静态变量。

2019-12-30 0 评论 47 浏览
阅读全文

之前读完《重构,改善代码既有设计》一书,书中内容虽然简单,但却有效。一些小小的重构积累起来便使得我们的代码开始变得更优雅。印象很深的是作者倡导的事不过三的原则,代码出现三次重复即需要进行重构。我深有体会,经常觉得自己的某些设计不好,但是又跟自己说下次再一起重构吧?结果日复一日需要重构的设计越来越多,导致重构的成本越高,越不敢重构。这篇文章再温习一下重构一书中的内容。

尽信书不如无书。书中只是建议并非标准,有些时候我们需要具体情况具体分析。

代码的坏味道

1. 重复代码

重复代码意味着冗余,当重复的代码需要修改时要修改所有重复的地方,稍微疏忽便会出现bug。

2. 过长函数

当函数过长时,人很难一下掌握太多的细节,使得修改这个函数的成本很高。

3. 过大的类

单个类承担的责任过多,违反单一职责而形成过大的类。

4. 过长的参数列

长的参数列简直是噩梦,因为稍有不慎将类型相同的参数填错顺序就引发Bug了。(好在IDEA编译器能对填入的每个参数提示参数名)

5. 发散式变化

就是类受多个变化的影响,是违反迪米特法则的结果。类应该尽可能少的与其他类打交道,避免非必要的关联。

6. 霰弹式修改

单个变化引发多个类的相应修改。

7. 依恋情节

当一个类的行为严重依赖其他类的时候,我们需要思考这个行为真正的归属。

8. 数据泥团

两个类中相同的字段或者方法签名中相同的参数总是一起出现,可能这些字段可以自成一类。

9. 基本类型偏执

此基本类型并不是指int、long这类,而是指类中出现很多小字段,比如省、市、区、住址可以封装成一个地址对象作为User类中的一个属性。

10. Switch问题

当出现Switch重复时,同样的Switch散布在不同的地方,增加一个新的case,需要找到所有Switch进行增加。

11. 平行继承体系

每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

12. 夸夸奇谈的未来性

过度设计。

13. 冗赘类

用处微乎其微以致于不如不作为一个类。或者作为内部类。

14. 令人迷惑的临时字段

类中的某些字段仅为特殊情况而定,或者类中的字段仅为了某个函数的方便声明为成员变量而没有其他用处。

15. 过度耦合的调用链

一个对象向另一个对象发起一个请求,再由另一个对象请求其他对象。

16. 中间人

无用的委托,过多中间层。

17. 狎昵关系

两个类关系过于紧密。一个类过于关注另一个类的成员,使得高耦合。

18. 异曲同工的类

不同的类或者函数,做着相同的事。

19. 不完美的类库

类库不能满足实际开发需求。

20. 纯数据类

类似于DDD中所阐述的贫血模型,仅有数据没有行为的类。

21. 被拒绝的遗赠

子类继承了父类不必要的函数或者数据。

22. 过多的注释

过多的注释说明代码的自解释能力很差。需要重构。


2019-12-30 0 评论 208 浏览
阅读全文

原文地址:《The JVM Architecture Explained 》

每个Java开发者都知道字节码是被JRE(Java运行时环境)所执行的。但是许多人并不知道实际上JRE是Java虚拟机(JVM)的实现。它分析字节码,解释代码并执行它。作为一个开发者,了解JVM的体系结构是非常重要的,因为它使我们能更有效的编写代码。在这篇文章中,我们将更深入的了解Java中的JVM体系结构和JVM的不同组件。

什么是JVM?

虚拟机是物理机器的软件实现。Java语言当时是基于编写一次到处运行的理念被开发出来的,其中到处运行指的是运行在虚拟机上。编译器将java文件编译成.class文件,然后.class文件被输入到JVM中,JVM会加载并运行这些.class文件。下图展示了JVM的体系结构。

JVM体系结构
JVMArchitecture.png

2019-12-22 2 评论 336 浏览
阅读全文

看到一篇关于Java内存模型的技术文章于是翻译一下供大家学习交流。

原文地址:《Java Memory Model》

java内存模型指定了java虚拟机如何与计算机的内存(RAM)进行工作。java虚拟机是一整个计算机的抽象模型,所以这个模型天然的包括了内存模型,它被称之为java内存模型。

如果你想设计正确运作的并发程序的话,那么理解java内存模型是非常重要的。Java内存模型指定了不同线程如何以及何时可以看到其他线程写入共享变量的值,以及在必要时如何同步对共享变量的访问。
在最初版本的Java内存模型是不足的,所以在Java 1.5版本中进行了修正。这个版本的Java内存模型沿用至Java 8中。

Java内存模型的内部

在JVM的内部使用了Java内存模型将内存划分给线程栈和堆。下图从逻辑视角说明了Java内存模型。

javamemorymodel1.png

每一个运行在虚拟机的线程都拥有一个它独有的线程栈。线程栈包含了关于线程方法调用中已到达当前执行点方法的信息。我们将它称之为调用栈。当线程执行到它的代码,它的调用栈就会改变。

线程栈同时包含了被调用的每一个方法的所有局部变量(所有方法都在调用栈上)。一个线程只能访问它自己的线程栈。局部变量除了创建它们的线程之外,对于其他线程都是不可见的。甚至两个线程正在执行同一段代码,这两个线程仍然会创建代码中的局部变量在它们各自的线程栈之中。因此,每个线程都有每个自己版本的局部变量。

2019-12-11 0 评论 188 浏览
阅读全文
[译]Java NIO vs. IO 有更新!

英文原文地址:《Java NIO vs. IO》

当学习Java NIO和IO的API用法时,一个问题就从脑海里冒出:什么时候我们该用IO,什么时候我们该用NIO呢?在这篇文章中我会阐明Java NIO和IO之间的不同点和使用场景以及它们是如何影响代码的设计。

NIO和IO主要的不同点

以下的表格总结了NIO和IO之间主要的不同点。我会以更多的细节去阐明这些不同点在接下来的几节中。

IONIO
面向流面向缓冲区(Buffer)
阻塞IO非阻塞IO
Selector(选择器)

面向流与面向缓冲区

第一个不同点在于IO是面向流的而NIO是面向缓冲区的。所以这意味着什么?

Java IO是面向流的意味着你从一个流中一次读取一个或者多个字节。如何处理读出的字节取决于你。它们不会缓存到任何地方。此外你不能在流中来回移动数据。如果你需要来回移动从流中读取的数据,你需要将其先缓存到一个缓冲区中。

Java面向缓冲区的方法稍微有点不同。数据先被读入到一个缓冲区,然后再处理。你也可以将你的数据在缓冲区中来回移动。这使得你在处理过程中多了一些灵活性。但是,你必须检查缓冲区内是否包含了完整处理过程所需的所有数据。并且你必须确保当读入更多数据到缓冲区时,不能覆盖尚未处理的数据。

2019-12-17 0 评论 215 浏览
阅读全文
随机洗牌的算法 有更新!

今天偶然看到群里的群友说道,面试被问如何将扑克牌随机洗牌输出。笔者觉得这道题挺有意思而且挺开放性,有多种不同的实现方式。然后我就随手写了一个算法,仔细一想这个算法的优化空间挺大,于是又写出三种算法。

第一种

我们通过JDK的随机算法获取一个随机下标,再通过Set集合来判断牌是否有被抽到过,如果抽到过的话,继续进行循环,直到抽到原牌数量为止。

public class ShuffleCard1 {

    public static int[] getShuffleCards(int[] cards) {
        // 获取随机数种子
        Random rand = new Random(System.currentTimeMillis());
        // 用Set集合存储已抽过的牌
        Set<Integer> isExisted = new HashSet();
        // 声明洗牌后数组
        int[] shuffleCards = new int[cards.length];
        // 已抽到的牌数量
        int drawCount = 0;
        // 当抽到的牌数量没达到原牌数组的大小时循环
        while (drawCount < cards.length) {
            // 获取一个随机下标
            int index = rand.nextInt(cards.length);
            // 判断该下标对应的牌是否已被抽过,没有的话,抽出
            if (!isExisted.contains(cards[index])) {
                shuffleCards[drawCount++] = cards[index];
                isExisted.add(cards[index]);
            }
        }
        return shuffleCards;
    }
}
2019-09-17 1 评论 455 浏览
阅读全文