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

面试 标签

求二叉树中节点的最大距离

情况A: 路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
方案:计算两个节点到根节点的深度相加。
情况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
方案:计算两个节点到子树根节点的深度相加

fibonacci数列的动态规划算法?

利用空间换时间。使用临时变量保存之前计算好的值。

分配饼干问题?每个孩子都有一个满足度,每个饼干都有一个大小,只有饼干的大小大于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。Input:[1,2], [1,2,3]。Output: 2

贪心算法。对饼干、孩子满足度进行排序。然后先用小饼干给满足度低的孩子。不造成浪费。

如何计算二叉树的深度

public static int treeDepth(TreeNode root) {
    if (root == null) {
        return 0;
     }
    // 计算左子树的深度
    int left = treeDepth(root.left);
    // 计算右子树的深度
    int right = treeDepth(root.right);
    // 树root的深度=路径最长的子树深度 + 1
    return left >= right ? (left + 1) : (right + 1);
 }

如何打印二叉树每层的节点?

  • 借助一个队列,先把根节点入队,每打印一个节点的值时,也就是打印队列头的节点时,
  • 都会把它的的左右孩子入队,并且把该节点出队。直到队列为空。

二叉树的Z型遍历?

借助一个队列和一个栈,方法和打印层遍历类似,区别在于隔层利用栈来逆序遍历。

反转单链表?

  1. 可以利用栈逆序拼接。
  2. 利用三个指针引用,一次性遍历反转,在每次反转两个节点的时候,都需要保存下一个节点,以免丢失原链表。

随机链表的复制?

复制成 1->1'->2->2'->3->3' 的链表,然后再拆分出来 1'->2'->3'

2020-03-29 0 评论 14 浏览
阅读全文

★redis的主从复制怎么做的?

Redis主从复制可以根据是否是全量分为全量同步和增量同步。以下对其相应的同步过程及原理做下简要说明。
增量同步

Redis增量同步主要指Slave完成初始化后开始正常工作时,Master发生的写操作同步到Slave的过程。通常情况下,
Master每执行一个写命令就会向Slave发送相同的写命令,然后Slave接收并执行。
全量同步

Redis的全量同步过程主要分三个阶段:

同步快照阶段:Master创建并发送快照给Slave,Slave载入并解析快照。Master同时将此阶段所产生的新的写命令存储到缓冲区。
同步写缓冲阶段:Master向Slave同步存储在缓冲区的写操作命令。
同步增量阶段:Master向Slave同步写操作命令。

★redis为什么读写速率快性能好?

纯内存操作。
核心是基于非阻塞的 IO 多路复用机制。
C 语言实现,一般来说,C 语言实现的程序“距离”操作系统更近,执行速度相对会更快。
单线程反而避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。

redis为什么是单线程的?

  • 我们要去理解为什么多线程好,多线程好在IO慢的时候才能凸显出高性能,因为在一个线程等待IO的时候马上切换线程运行。
  • 而在Redis中数据都在内存当中,IO非常快,如果还是采用多线程的话,反而会浪费时间在线程的上下文切换。

缓存的优点?

  1. 减少了对数据库的读操作,数据库的压力降低
  2. 加快了响应速度

2020-03-12 5 评论 43 浏览
阅读全文

  • 标★号为重要知识点

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 评论 41 浏览
阅读全文

  • 标★号为重要知识点

★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 评论 34 浏览
阅读全文

  • 标★号为重要知识点

说一下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 浏览
阅读全文

  • 标★号为重要知识点

★有一个组合索引(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 浏览
阅读全文

  • 标★号为重要知识点

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中有哪些代理模式?

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

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

装饰器模式、适配器模式

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

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

介绍一下策略模式?

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

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

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

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

标星号的为常用设计模式

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

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

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

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 浏览
阅读全文

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

介绍一下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 浏览
阅读全文

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

★为什么重写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 评论 43 浏览
阅读全文