Java面试总结

一、Java基础

1、Java语言特性#

简单易学、面向对象,可靠安全,跨平台,支持多线程。

2、面向对象三大特性#

封装,继承,多态。

封装就是使用private关键字修饰变量和方法,使得它们只能够内部调用,如果外界想要调用的话,需要调用本类提供的对外开放的接口。

继承的关键字是extends,当A extends B 的时候,就代表A类继承了B类。通过继承,子类可以继承到父类的属性和方法(private关键字修饰的属性和方法无法被继承),子类也可以对继承过来的父类方法进行方法重写。一个父类可以有多个子类,但是一个子类只能有一个父类。

多态是指同一个行为具有多种不同表现形式的能力。通常通过父引用=子对象的方式实现,也可以子引用=父对象的方式实现,但是这种实现方式必须进行向下转型。

3、方法重写和方法重载#

方法重写就是子类继承了父类的方法,但是子类不想原封不动的继承父类的方法,所以在方法名,返回值列表和参数名相同的情况下,对方法体进行修改或重写。

方法重载是指同名的方法有不同的参数列表即可构成方法重载,和返回值类型同与不同没有关系。

4、Java基本数据类型#

byte,short,int,long,float,double,char,boolean。

5、==和equals的区别#

==是根据地址来进行比较,Object类中的equals的底层也是通过==来进行比较的,在没有其他方法重写equals的情况下,equals是通过地址来进行比较的。

6、String,StringBuffer,StringBuilder#

三者都是引用型数据类型。String底层使用final关键字修饰的数组,长度不可变。StringBuffer线程安全,使用了Synchronized锁来保证线程安全,但是速度稍慢,StringBuilder线程不安全,但是速度快。二者底层均为可变数组。

7、final关键字的作用#

被final修饰的类不可以被继承,被final修饰的方法不能够被重写,被final修饰的变量值不可变,被final修饰的方法,JVM会尝试将其内联,以提高运行效率。被final修饰的常量,在编译阶段会存入常量池中。

8、Static关键字的作用#

Static可以修饰变量和方法。被修饰的变量叫静态变量,可以直接使用类名.出来。还有也可以修饰代码块、内部类和包等。

9、访问修饰符的区别#

其他包,子类,同包,本类。

10、关于异常#

try...catch...:在try代码块中可能会发生异常,一旦发生异常,有catch来捕获。

throws:抛出异常,但不会进行处理,谁调用该方法谁来处理。

finally:无论有没有异常,都会执行finally代码块中的内容。

throw:通过throw new exception,我们自主地抛出一个异常。

如果try中含有return,则先执行finally中的代码,再执行return。

二、Java集合

1、HashMap的底层原理?#

JDK1.8以前,HashMap底层是通过数组+链表的方式存储数据的,链表中的数据采用头插法来进行存储。

JDK1.8,HashMap底层采用数据+链表+红黑树的方式进行数据存储,链表采用尾插法。当链表长度大于8时,自动转为红黑树。当链表长度小于6时,自动转回链表。

2、HashMap的扩容原理?#

HashMap底层数组初始化长度为16,加载因子默认为0.75,也就是当底层数组长度大于12的时候会自动扩容,扩容到原来的二倍。

3、HashMap如何进行插入的?#

当使用put方法对HashMap进行数据添加的时候,先调用hashcode()方法对插入的key做hash运算,计算出hash值,再对16进行求余,该余数就是插入数据的位置。

当数据插入时,先判断插入的数组位置上是否有数据,如果有数据,先调用hashcode进行运算,比较二者key的哈希值是否相等,如果不等,则插入到该位置的链表或红黑树上,如果相等,则再调用equals方法进行key的值运算,如果不等,则插入到该位置的链表或红黑树上,如果相等,则进行值的覆盖。

4、Java主要的集合都有什么?#

Collection:为所有单列集合的父接口,其子接口有List,Set。

Map:为所有双列集合的父接口,其子接口有HashMap,HashTable,TreeMap,ConcurrentHashMap。

List:为单列无序可重复集合。其子接口有ArrayList,LinkedList,Victor。

Set:为单列有序不可重复集合。其子接口有HashSet,TreeSet。

ArrayList:底层为Object类型的数组。

LinkedList:底层为双向链表。

Victor:为线程安全的ArrayList。因为其有synchronized线程锁。

HashSet:底层是HashMap,通过调用hashcode()和equals()方法来实现不可重复。

TreeSet:底层是二叉树。可以实现自然排序,默认是自然排序,也可以自定义排序规则。

HashMap:key无序,可以为空。只能为引用数据类型。线程不安全。

HashTable:是线程安全的HashMap。因为其有synchronized。key不能为空。

TreeMap:基于二叉树实现的,可以对key进行排序,线程不安全。

ConcurrentHashMap:线程安全。对整个Hash桶采用分段策略,拆分成若干个断segment,对每一个段上锁,极大的提高了并发修改效率。

三、Java线程

1、Java中实现线程的方式#

继承Thread类,实现Runnable接口,实现Callable接口,通过线程池来创建线程。

其中Runnable接口无返回值,Callable接口有返回值。

线程池的核心参数:

corePoolSize:线程池核心线程大小。

maxinumPoolSize:线程池最大线程数量。

keepAliveTime:空闲线程存活时间。

unit:空闲线程存活时间单位。

workQueue:工作队列。

threadFactory:线程工厂。

handler:拒绝策略。

2、Volatile关键字#

volatile关键字修饰的变量,可以实现线程之间的可见性,避免变量脏读的情况出现,底层是通过限制jvm指令重排序实现的,适合于一个线程修改,多个线程读的场景。

3、Redis锁机制#

Redis锁机制简而言之就是悲观锁和乐观锁的机制。总的来所就是悲观锁阻塞事务,乐观锁回滚重试。

乐观锁是一种思想,它的思想就是当一个事务操作共享数据,我们乐观的认为这期间不会有其他事务再来操作该区域的共享数据,所以乐观锁并不加锁。具体的实现就是我们可以给表中加一个版本号的字段,每当有一个事务操作完成共享数据,版本号就加一,同样的,当事务操作完共享数据时,需要查看版本号是否发生了变化,如果发生了变化,则回滚事务。

悲观锁是数据库实现的,它悲观的认为当一个线程操作贡献的时候,肯定会有其他线程也来操作。所以就给该共享数据加锁。之后来的线程只能阻塞等待。待前一个线程操作结束,CPU再随机分配阻塞的线程操作共享数据。

关于它们的实现:

悲观锁通过for update来实现

select ... for update

例如:该语句锁住了student表中age=20的所有记录。在本次事务提交之前,外界无法修改这些记录。

select * from student where age = 20 for update

乐观锁只需要加一个版本号的字段即可。

四、Spring全家桶

1、常用注解#

  • SpringCloud组件相关

@EnableEurekaServer:启动eureka服务。

@EnableEurekaClient:启动eureka客户端。

@EnableHystrix:启动熔断器。

@FeginClient:启动远程调用接口。

@RibbonClient:启动负载均衡。

  • Springboot相关

@SpringbootApplication:springboot的主启动类。可采分为:

@SpringbootConfiguation:表明该类为配置类,其根注解为:

@Configuation:表明该类为配置类。

@EnableAutoConfiguation:开启自动注入功能。

@CompentScan:开启包扫描机制。

  • SpringMVC相关

@RequestMapping:建立url与后台方法之间的对应关系。

@ResponseBody:将方法的返回值以json的格式写入到response的body中,返回给客户端。

@RequestBody:用户获取请求体的内容。

@RequestParam和@RequestVariable:用于从request中接收请求,不过@RequestParam是从request中取值,而@RequestVariable直接从url上取值。

@RestController:是@Controller和@ResponseBody的结合。实现Restful风格的接口并返回json字符串。

  • Spring依赖注入相关

@Autowired byType注入依赖。

@Resourse byName注入依赖。

@Qualify 和@Autowired联用,通过byName进行依赖注入。

@Controller,@Service,@Respository,@Mapper(由JDK提供)

2、Get和Post的区别#

get和post都是向浏览器提交数据,并且都会从浏览器获取数据。

区别在于:

  • 传输方式:get通过地址栏传输,post通过报文传输。
  • 传输长度:get长度有限制,post长度无限制。
  • 产生TCP数据包的数量:get一次,post两次。
  • 安全性:get安全行差但是快,post安全性高但是慢。

3、Spring的IOC#

IOC即控制反转,通常我们在项目中需要手动去new对象,这种方式不利于对对象的管理,现在我们将对象的创建权反转给spring容器,这就是控制反转。Spring容器在实例化对象的时候,会根据对象的直接依赖关系,自动完成属性的注入工作,这就是依赖注入。

4、Spring容器创建对象的方式#

  • 通过类中的无参构造创建(默认)
<!--id是引用名称,class是类的路径-->
<bean id="userDaoImpl" class="com.lgy.dao.UserDaoImpl"></bean>
<bean id="oracleDaoImpl" class="com.lgy.dao.UserDaoOracleImpl"></bean>
<bean id="userDaoMySqlImpl" class="com.lgy.dao.UserDaoMySqlImpl"></bean>
  • 根据构造器中参数下标来创建
<bean id="user" class="com.lgy.pojo.User">
    <!--下标为0的属性赋值为张三,也就是构造方法中第一个参数-->
    <constructor-arg index="0" value="张三"/>
</bean>
  • 根据构造器中参数的类型来赋值
<bean id="user" class="com.lgy.pojo.User">
    <constructor-arg type="java.lang.String" value="张三"/>
</bean>
  • 根据构造器中参数名称来赋值
<bean id="user" class="com.lgy.pojo.User">
    <constructor-arg name="name" value="张三"/>
</bean>

5、Spring中依赖注入的方式#

  • 通过构造器注入(上述)
  • 通过set方法注入
<bean id="student" class="com.lgy.pojo.Student">
    <property name="name" value="张三" />
</bean>
  • p和c的空间注入:p和c的空间注入就是对Set方法注入和构造器注入的简化。其中p-->set,c-->构造器。
<bean id="user-p" class="com.lgy.pojo.User" p:id="1" p:name="张三" p:sex="男" />
    <bean id="user-c" class="com.lgy.pojo.User" c:id="2" c:name="李四" c:sex="男" />
  • 使用注解实现自动依赖注入。

6、SpringAOP怎么用的?#

AOP即面向切面编程,底层是通过动态代理的机制实现的,支持jdk和cglib两种。Spring框架默认是通过jdk动态代理,但是springboot默认是通过cglib来实现动态代理的。通常我们在项目中,一些公共的功能可以通过aop来进行解耦合和统一实现。例如:事务管理,日志,权限等。

我们在项目中的事务是这样设置的:

首先声明spring的事务管理器transcationManager,之后配置一下事务的传播特性。tx:advice tx:method。最后配置切面,通常我们切面设置在service实现类这一层。pointcut。

7、Spring中所涉及到的Java设计模式#

  • 工厂模式:Spring中的BeanFactory就是工厂模式。

  • 单例模式:Spring依赖注入Bean实例默认是单例的。

  • 适配器模式:SpringMVC中的适配器HandlerAdatper。

  • 代理模式:Spring的AOP就是动态代理。

  • 观察者模式:Spring的事件驱动模型就是观察者模式,最常使用在listener中。

  • 建造者模式:SqlSessionFactoryBuilder中使用。

8、MyBatis中常用的标签#

select、update、delete、insert、sql、where、tirm、choose、when、if等。

9、数据库连接池的优点#

没有数据库连接池的时候,我们要想操作数据库,需要每次都和数据库建立一个连接,用完之后再关闭连接,这样频繁的创建连接、关闭连接,对数据库的消耗过大。而使用了数据库连接池之后,它启动的时候就会与数据库创建连接,连接的数量可以自定义,当应用想要与数据库交互的时候,直接就根数据库连接池建立连接即可,而不用去找数据库,这样数度比较快,消耗资源少。

10、SpringBoot的自动配置原理#

当主类标识@SpringBootApplication注解时自动扫描当前的包及其子包。开启自动配置之后,Spring会查找spring-boot-autoconfigure jar包下mete-inf文件夹下的spring.factories

五、Mysql数据库

1、笛卡尔积#

当数据库进行连表查询时,没有加任何限制条件,查出的数据为两表数据之和。如下:查出的数据应为25条。

select * from course,student;

2、隐式内连接#

查出number为NO1001人的姓名

select u.`name`,w.number from `user` u,work_card w where u.id = w.user_id and w.number='NO1001';

3、显示内连接#

显示内连接语法:from 表A inner join 表B on 连接条件。

查出number为NO1001人的姓名

select u.`name`,w.`number` from `user` u inner join `work_card` w on u.id = w.user_id and w.number = 'NO1001';

4、左外连接#

左外连接语法:from 表A left join 表B on 连接条件。

查出number为NO1001人的姓名

select u.`name`,w.`number` from `user` u left join `work_card` w on u.id = w.user_id and w.number = 'NO1001';
  • 执行结果

5、右外连接#

左外连接语法:from 表A right join 表B on 连接条件。

查出老张三的money。

select u.`name`,w.`money` from `user` u right join `work_card` w on u.id = w.user_id and u.`name` = '老张三';

6、多对多关系如何查询?#

多对多关系需要各取两表的主键,来建立一个中间表,进行三表联查。

7、Mysql关键字#

  • distinct:去重
select distinct name from `job`;
  • group by:分组查询

找出不同工作类别中的最高工资

select job,max(sal) from emp e group by e.job;
  • limit:分页,limit(起始页数,每页条数)

  • 子查询:相当于嵌套查询。

查询每个部门的最高薪水:要求显示最高薪水人员姓名

# 查询每个部门的最高薪水
select deptno,MAX(sal) as maxsal from emp group by deptno;

#再将查询结果作为临时表,和emp表进行联合查询
select e.ename,t.* from emp e inner join (select deptno,MAX(sal) as maxsal from emp group by deptno) t on e.deptno = t.deptno where e.sal = t.maxsal order by deptno asc;

8、内连接,左外连接,右外连接的区别#

内连接的连接结果仅包含符合条件的连接,而左外连接则是以左表为主表,将左表的字段数据全部打印出来,而右表若是不存在则记为空。反之,右连接则是以右表为主表,而左表若是不存在则记为空。

六、数据库优化

1、你是怎么设计数据库的?#

设计数据库首先要遵循三大范式原则要求:原子性,一致性,依赖性。

原子性是指数据库的每一列都是不可分割的原子数据项。

依赖性是指实体属性完全依赖于主关键字。

关联性是指任何非主属性不依赖于其他非主属性。

对于数据库来说,首先要考虑范式的要求,其次为了提高效率,允许有一些冗余的字段。设计数据库的时候,要预估单表的峰值。以mysql为例,如果单表的数据超过千万条,查询速度就特别慢了,这里就需要对数据库进行分库分表,为了提高查询和写入性能,mysql可以采用主从结构进行读写分离。

2、数据库索引是什么?#

索引是数据库提供的快速查询机制,当索引建立了之后,硬盘会生成索引文件。当查询的时候,数据库就会取索引文件中找到具体满足查询条件数据的物理位置根据位置就可以快速获取数据,避免了全表扫描。

3、索引的种类及建立方式#

# 主键索引
alter table 表名 add primary key(字段名);
# 普通索引
alter table 表名 add index ids(字段名);
# 唯一索引
alter table 表名 add unique index uniq_idx(字段名);
# 全文索引
alter table 表名 add fulltext index full_idx(字段名);

4、索引的最左原则#

最左原则发生在组合索引中,例如给a,b,c三个字段建立组合索引,那么a在最左边,当查询条件where后面有a,则索引就会生效,没有a,则索引就会失效。

select * from table where a=? and b=? and c=?; # 生效
select * from table where b=? and c=?; # 失效

5、对于MySQL慢查询及调优#

对于MySQL慢查询,故名思意,就是很慢的查询操作,至于具体的定义,我们通常设计一个事件阈值,超过了这个事件阈值的查询就叫做慢查询。此时,我们对慢查询的sql语句做sql优化,核心原理就是避免全表扫描和索引失效。

6、如何避免索引失效#

  • 不要对字段进行null值判断。
  • 不要使用不等于,大于,小于。
  • 不要使用or来连接条件。
  • 不要使用in和not in,使用between来取代。
  • 模糊查询%不要放在前面。

7、索引有什么优缺点?#

索引可以提高查询效率,但是对于增删改操作,由于数据库需要维护索引文件,如果频繁增删改的化,影响数据库的性能。我们在平时设计数据库的时候,只针对那些频繁查询的字段来创建索引,通常情况下创建索引的列不会超过6个。

8、MySQL的存储引擎有哪些#

InnoDB:支持事务,支持外键,主要用于主从读写分离的主服务器。

MyIsam:对大规模读写比较好,主要用于从服务器,不支持事务,不支持外键。

七、Redis

1、Redis的基本数据类型#

String,Hash,Set,ZSet,List。其中Set不能排序,ZSet可以排序。

2、Redis项目中部署几台服务器?#

3主3从。最少两个,一主一从。

3、Redis在项目中的使用#

  • 项目中的字典表数据,通过redis进行缓存,提供查询功能,我们使用spring-cache整合redis实现分布式缓存。
  • 短信的验证码存放在redis中,利用redis的过期策略,2分钟内有效。
  • 使用redis充当session容器,使用spring-session来整合redis。
  • 使用redis来存储购物车信息。

4、Redis的雪崩、击穿和穿透#

雪崩是指短时间内redis中的key大面积失效,导致大量的请求全打在了数据库上,导致数据库瘫痪。解决办法就是给数据的key设置随机的过期时间即可。

击穿是指一些热点数据,它经常被使用,而却突然到期了,这时又来了大量的请求,这些请求全打在了数据库上,倒是数据库瘫痪。解决办法就是设置热点数据永不过期,或者加互斥锁。

穿透是指缓存和数据库中都没有的数据,而用户还在不断的请求,比如说id为-1或者是一个特别大的数,这样绕过了缓存直接请求到了数据库,使数据库压力过大。解决办法就是接口层增加校验,或是当请求的数据缓存中没有、数据库中也没有的时候,自动生成一个key-null的键值对给redis,设置30秒的过期时间。

5、Redis的持久化操作#

Redis的持久化操作主要包括RDB和AOF两种方式,可以单独使用其中一种或者将两种结合使用。

RDB方式是通过快照来完成的。当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘中。我们可以通过redis.conf来查看和修改配置,找到save,save 900 1 就代表者每900秒如果就一个修改就将redis中的数据写入数据库。可以配置多个save。

AOF方式需要我们手动开启持久化,可以通过appendnoly yes 参数来开启AOF。开启AOF持久化后没执行一条更改Redis中的数据命令,Redis就会将该命令写到硬盘的.aof文件中,可以通过appendfilename来进行修改。

通常我们在项目中默认使用RDB方式,同时也会手动开启AOF来保证数据的完整性。

八、前端

1、AJXA#

$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  async:false //设置同步和异步
  data: {
    "username": "Q1mi",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})

九、Git

1、Git常用命令#

  • 复制远程仓库中的项目到电脑本地。
git clone
  • 将本地的项目保存在暂存区
git add
  • 将项目提交到本地仓库
git commit -m
  • 将本地项目上传至远程仓库
git push
  • 拉取远程仓库的代码到本地
git pull

十、Linux

1、Linux常用命令#

  • 解压JDK
tar -zxvf jdk.rar.gz
  • 使用yum安装mysql
yum install mysql
  • 查看java以及进程端口号
ps -ef|grep java
  • 查看日志
tail -f
  • 新建一个文件夹
mkdir
  • 新建一个文本
vim
  • 退出文本
esc:wq

十一、中间件

1、RabbitMQ消息不丢失#

消息丢失很可能在生产者,mq服务,消费者任意一个环节出现,生产者可以使用channel事务机制来保证消息传递的可靠性,但是吞吐量会下降,这个时候可以使用confirm机制,mq收到消息之后会进行ack回调通知,mq采用持久化对消息进行硬盘存储,可以保证mq异常关机之后数据不会丢失;消费者关闭rabbitMQ提供的自动ACK消息确认机制,改为消费者处理完消息之后,手动ACK。

posted @   lgyy  阅读(161)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示
主题色彩