NickDwade

 

精选面试题(不分章节)

面向对象

相较于面向过程来理解,举个例子

洗衣服

  面向过程:一、放衣服 二、洗衣服

  面向对象:人:放衣服 洗衣机:洗衣服

面向过程比较直接高效,面向对象更易于复用,维护和扩展

三大特性:封装继承多态

封装的意义,在于明确表示出允许外部使用的所有成员函数和数据项

内部细节对外部调用透明,外部调用无需修改或者关心内部实现

1、javabean的属性私有,提供getset对外访问,因为属性的赋值或者获取逻辑只能由JavaBean本身决定

2、orm框架

  操作数据库,我们不需要关心链接是如何建立的、sql是如何执行的,只需要引入mybatis,调方法即可

继承:继承基类的方法,并作出自己的改变或者扩展

子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性化的

多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同

继承,方法重写,父类引用指向子类对象

无法调用子类特有的功能

 拦截器和过滤器的区别

1. 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

JDK、JRE、JVM区别和联系

JDK:

Java Develpment Kit java 开发工具

JRE:

Java Runtime Environment java运行时环境

JVM:

java Virtual Machine java 虚拟机

 

 

 JDK包含了JRE和java工具

JRE包含了jvm和lib(类库)

==和equals

==对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址

equals默认也是采用==比较,通常会重写,比如String,把字符串的每一个字符拿出来相比

 

 

 简述final作用,为什么局部内部类和匿名内部类只能访问局部final变量

最终的

作用:

  修饰类:表示类不可被继承

  修饰方法:表示方法不可被子类覆盖,但是可以重载

  修饰变量:表示变量一旦被赋值就不可以更改它的值

外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能是还存在的。这里就出现了一个矛盾:内部类对象访问了一个不存在的变量。

为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍然可以访问它

 

 String、String Buffer、StringBuilder区别以及使用场景

String是final修饰的,不可变,每次操作都会产生新的String对象

StringBuffer和StringBuilder都是在原对象上操作

StringBuffer是线程安全的,StringBuilder线程不安全的

StringBuffer方法都是synchronized修饰的

性能:StringBuilder>StringBuffer>String

 

场景:经常需要改变字符串内容时使用后面两个

优先使用StringBuilder,多线程使用共享变量时使用String Buffer

重载和重写的区别

重载:发生在同一个类中,方法名必须相同、参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时

重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法

 

 接口和抽象类的区别

抽象类可以存在普通成员函数,而接口中只能存在抽象public abstract方法

抽象类的成员变量可以是各种类型的,而接口的成员变量只能是public static final类型的

抽象类只能继承一个,接口可以实现多个

List和Set的区别

List有序可重复,按照对象进入的顺序保存对象,允许多个Null元素,可以用迭代器,可以用get获取下标获取元素

Set无序不可重复,允许一个Null,只能用迭代器获取所有元素,再逐一遍历

HashCode与equals

equals 是用来对比两个对象是否相等,重写equals,来定义两个对象的对比规则,默认object的equals,和==一样,对比的是引用地址

hashcode是获取哈希码,散列码,会返回一个int整数,对象存在堆中,怎样找到对象在堆中的哪一个位置呢,用哈希表,索引就是哈希码,哈希码可以快速找到对象在堆中的位置。

     散列表存储的是键值对,能根据键快速检索出对应的值

为什么要有hashcode

  用”hashset如何检查重复“来为例子,会先计算出对象的hashcode值,来判断对象加入的位置,看该位置是否有值,没有就放进去。如果发现有值,hashcode同样,就是哈希冲突。这时就会调用重写的equals来检查两个对象是否真的相同。如果相同,就不会让其加入成功,不同的话,就会特殊处理后重新散列。如果散列表的位置是空的,大大减少了equals的次数,相应就大大提高了执行速度。

重写了equals,如果不重写hashcode,那两个对象无论如何都不可能相等。hashcode的默认行为是对堆上的对象产生独特值

ArrayList和LinkedList的区别

 ArrayList

  基于动态数组,连续内存存储。下标访问,查询快,增删慢。

  插入慢:

      1、需要扩容,会新建数组,把老数组拷贝到新数组,再把老数组回收。

      2、增加到指定位置的话, 元素移位会复制

  可以解决:使用尾部插入法并且指定初始元素可以极大提升性能,甚至超过linkedlist(需要创建大量的node节点)

LinkedList

  基于链表,可以存储在分散的内存中。查询慢,增删快。只能由迭代器遍历,性能消耗大

HashMap和HashTable的区别?底层实现是什么?

HashTable

  线程安全,但是性能低,允许key和value为空

  实现是数组加链表,jdk8开始链表高度到8,数组长度超过64,链表转为红黑树,元素以内部类Node节点存在的

HashMap

  线程不安全,多线程执行结果有可能不一样

ConcurrentHashMap原理,jdk7和jdk8的区别

 线程安全版本的HashMap,一般不采用HashTable,效率较低,加了synchronized全局锁,ConcurrentHashMap采用的是分段锁

 JDK7

     底层是采用ReentrantLock+Segment+HashEntry,一个Segment包含一个HashEntry数组,每个HashEntry又是一个链表结构,类似HashMap,外层多了一个Segment

   分段锁,一个线程根据key在哪一段Segment,然后锁起来,锁的力度比HashTable要小一点,数组扩容也不会影响到其它Segment

   元素查询,二次Hash,一个Hash定位到Segment,第二次Hash定位到元素所在的链表的头部

    get方法无需加锁,volatile保证可见性

‘ JDK8

   底层是采用Synchronized+CAS+Node+红黑树,CAS是乐观锁机制,查找,替换,赋值操作都使用CAS

   锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容

   读操作无锁,Node的val和next使用voaltile修饰,读写线程对该变量互相可见,数组用volatile修饰,保证扩容时被读线程感知

如何实现一个IOC容器

1、配置文件配置包扫描路径

2、递归包扫描获取.class文件

3、反射.class文件,确定需要交给IOC管理的类

4、对需要注入的类进行依赖注入

 什么是字节码?采用字节码的好处是什么?

 如何判断对象可以被回收?

  引用计数法

    被引用一次增加一次,为零就回收。效率比较高,但解决不了循环引用。

  可达性分析

    从GC Roots下开始搜索,往下是一条引用链路。当一个对象没有被任何引用链路相连时,证明对象是不可用的。

      GC Roots是虚拟机栈中引用的对象,静态属性引用的对象,常量引用的对象,本地方法栈中native引用的对象

    可达性分析要经历两次标记过程,第一次是发现没有在引用链路中的对象就标记起来,第二次判断是否覆盖finalize方法,如果重写就执行,没有就执行回收。执行finalize方法后会重新进行可达性分析。

线程的生命周期?线程的哪些状态?

  五种状态,新建,就绪,运行,阻塞和死亡状态

    阻塞的状态分为三种

      等待阻塞:执行wait方法,等待notify或者notifyall唤醒

      同步阻塞:同步锁被别的线程占用,会把该线程放入锁池中

      其他阻塞:运行的线程执行sleep或者join方式时

  新建状态(New):新建了一个线程对象

  就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权

  运行状态(Running):就绪的线程获取了CPU,执行程序代码。

  阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。知道线程进入就绪状态,才有机会转到运行状态。

  死亡状态(Dead):线程执行完了或者因为异常原因退出了run方法,该线程结束生命周期

SpringBoot的常见注解?

@Component:放在类上,把普通类实例化到spring容器中。可以说很多注解都是基于这个注解的。

@Bean: 放在方法上,用@Bean标注方法等价于XML中配置bean,这个方法一般返回一个实体对象,告诉spring这里产生一个对象,然后这个对象会交给Spring管理。产生这个对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的容器中。

@Configuration:标注当前类是配置类,并会将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到srping容器中,并且实例名就是方法名。(其实就是靠@Component注解)

@RestController、@Controller、@Service、@Repository

@Resource:是按照名称来注入的,当找不到与名称匹配的bean才会按照类型来注入。其实我们平时用的@Resource都是用了他的默认的方式,即都不指定名字和类型。spring通过反射机制使用byName方法自动注入

@Autowried:默认是按照类型进行装配注入,如果允许 null 值,可以设置它 required 为false。即:当不能确定 Spring 容器中一定拥有某个类的 Bean 时,可以在需要自动注入该类 Bean 的地方可以使用 @Autowired(required = false) ,这等于告诉 Spring:在找不到匹配 Bean 时也不报错。

@SpringBootApplication:这个注解就是集成了:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解。其中@SpringBootConfiguration:表示这个类为配置类;@EnableAutoConfiguration:表示开启自动配置,我们平时所说springboot无配置就是这个参数起的作用,他读取了springboot默认的配置;@ComponentScan:表示自动扫描,这个扫描默认只能扫同一级的目录。

@Autowried作用

@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。

activemq消息重复问题怎么解决

一般解决重复消息的办法是,在消费端,让我们消费消息的操作具备幂等性。

用户登录,权限这块你是怎么做的?

就是设计表,有权限表,角色表,用户表。登录的时候进行用户的验证,判断是什么角色,拥有的权限。保存到session中

你们数据库中是怎样分库分表的

我们是按照用户id取模的方式分库分表的。

mybatis和orm框架有什么区别?

与其他比较标准的ORM框架(比如Hibernate)不同,mybatis并没有将java对象与数据库关联起来,而是将java方法与sql语句关联起来,mybatis允许用户充分利用数据库的各种功能,例如存储、视图、各种复杂的查询以及某些数据库的专有特性。

 

Mybaits为什么是半orm框架?

Hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成SQL。而MyBatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写SQL来实现和管理。

Spring怎么将类放进ioc容器

配置xml文件

使用注解component并要扫描所在的包

手动写配置类,configuration类中用bean注解

pageHelper分页原理

 

  PageHelper方法使用了静态的ThreadLocal参数,分页参数和线程是绑定的。内部流程是ThreadLocal中设置了分页参数(pageIndex,pageSize),之后在查询执行的时候,获取当线程中的分页参数,执行查询的时候通过拦截器在sql语句中添加分页参数,之后实现分页查询,查询结束后在 finally 语句中清除ThreadLocal中的查询参数
MylSAM和innoDB的区别

InnoDB支持事务,MyISAM不支持

InnoDB支持外键,而MyISAM不支持。

InnoDB是聚集索引,使用B+Tree作为索引结构。

InnoDB表必须有唯一索引(如主键)(用户没有指定的话会自己找/生产一个隐藏列Row_id来充当默认主键),而Myisam可以没有

left join、inner join、right join的区别

  -left join(左联接) 返回包括 左表中的所有记录和右表中联结字段相等的记录 
  -right join(右联接) 返回包括 右表中的所有记录和左表中联结字段相等的记录

  -inner join(等值连接) 只返回两个表中联结字段相等的行

数据库常见的索引

  唯一索引:UNIQUE 

  主键索引  primary key 即唯一+非空

  聚集索引(又叫聚簇索引):cluster

数据库的锁

表级锁
表级别的锁定是 MySQL 各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。

所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。

当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并发大度大打折扣。

行级锁
行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。

由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。

虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。

页级锁
页面锁会去锁定一页的数据,我们知道 MySQL 的索引本身是由 B+ 树实现的。

每个叶子节点的单位页,叶子节点上面存放了多个记录行,数据存储是按照一页一页来的,每次锁定一页的数据,其实就是相邻的数据,开销和加锁时间界于表锁和行锁之间。

页级锁也会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
数据库如何进行分页查询

oracle使用rownum,mysql使用limit

使用索引为什么能提高查询速度

索引就是通过事先排好序,从而在查找时可以应用二分查找等高效率的算法。

比如表中有一百万条数据,需要在其中寻找一条特定id的数据。如果顺序查找,平均需要查找50万条数据。而用二分法,至多不超过20次就能找到。二者的效率差了2.5万倍!

sleep和wait的区别

wait会把锁释放掉,放入等待池中,notify会随机放一个等待池的进入锁池,notifyall会放所有

  sleep是Thread类中的,wait是obj类中的

  sleep不会释放锁,wait会释放锁

  sleep不依赖于synchronized,wait需要

  sleep用于当前线程休眠,wait则用于多线程之间通信

  sleep会让出cpu执行时间且切换上下文,wait不一定,还有机会重新竞争到锁继续执行

对线程安全的理解

 不是线程安全,应该是内存安全,堆是共享内存,可以被所有线程访问。单线程执行和多线程执行结果是一样的,线程就是安全的 

对守护线程的理解

  为用户线程提供服务的线程,用户线程全部结束,守护线程就会结束

  作用:GC垃圾回收线程,不再有任何运行的线程,垃圾回收线程无事可做,就会离开。始终在低级别的状态下运行,用于实时监控和管理系统中的可回收资源

 

posted on 2021-03-03 17:32  NickDwade  阅读(39)  评论(0)    收藏  举报

导航