java复习面试知识点

76 阅读12分钟

类的加载

类的加载器机制: 我们编写的java文件都是.java为后缀的,编译器把.java转化成.class文件,类的加载机制就是将.class中的二进制数据存到内存中,并对数据进行校验,解释,初始化。然后在方法区存放相对应的元数据,在堆中创建相对应的class对象

类加载的三个阶段:加载,连接(验证,准备,解释),初始化

双亲委派机制:如图

Snipaste_2023-02-06_22-44-50.png

I/O流 java中有几种类型的流?
字节流继承inputStream和OutputStream
字符流继承自Reade和Writer。
输入流就是从外部文件输入到内存,输出流主要是从内存输出到文件。

字节流和字符流的区别

字符流带有缓存,字节流没有。

字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。

字符流和字节流,一个属性范围小,一个属性范围大,字符流只能是字符这一种类型,但是字节流中可以是字符,可以是二进制文件,可以是音频,可以是各种各样的类型,只要符合字节形式存储的都可以接字节流,而字符流只能接字符。

集合

Java 容器都有哪些?

list:ArraryList, LinkedList

set;Treeset , hashset

map:hashmap ,hashtable,treemap

list与Set区别

(1)List,Set都是继承自Collection接口

(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)

(3)Set和List对比:

Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

了解ConcurrentHashMap吗?为什么性能比hashtable高,说下原理

ConcurrentHashMap是线程安全的Map容器,JDK8之前,ConcurrentHashMap使用锁分段技术将数据分成一段段存储每个数据段配置一把锁,即segment类,这个类继承ReentrantLock来保证线程安全,JKD8 的版本取消Segment这个分段锁数据结构,底层也是使用Node数组+链表+红黑树,从而实现对每一段数据就 行加锁,也减少了并发冲突的概率。

hashtable类基本上所有的方法都是采用synchronized进行线程安全控制,高并发情况下效率就降低 , ConcurrentHashMap是采用了分段锁的思想提高性能,锁粒度更细化

HashMap的put的流程

Snipaste_2023-02-01_21-15-06.png 如果是红黑树就直接插入树中,如果是链表会判断链表长度是否大于8,如果大于8就转为红黑树

ps:如果通过hash函数算出数组下标元素不为空需要判断key是否相等,如果相等会覆盖原来的key-value达到去重

ConcurrentHashMap的put流程

ConCurrentHashMap的put操作主要由putVal()方法实现,该方法中对value的插入,采用了CAS操作和synchronized的操作,从而保证了并发环境下的安全性。

put步骤大致如下:

判断key和value是否为null,如果是的话抛出NullPointerException并结束(ConCurrentHashMap不允许存放null型的key和value,这点和HashMap也不同) 通过key计算得到hashcode 判断是否需要进行初始化 利用hash值定位Node,如果当前位置没有Node,则依据CAS机制尝试插入。如果插入失败,则通过自旋保证插入成功 判断是否正在进行扩容,如果需要进行扩容,则执行helpTransfer方法(如果头节点是ForwardingNode类型,则表明正在扩容) 如果是在无需进行初始化,hash值计算得到的位置存在Node,并且无需扩容的情况下,则利用synchronized锁来写入数据(这个过程又会分为在普通链表中put和在红黑树中put) 上述操作后,如果当前数量超过了TREEIFY_THRESHOLD(8,跟HashMap中的值大小相同),则转化为红黑树结构。

线程的创建方式

1)继承Thread类创建线程

2)实现Runnable接口创建线程

3)使用Callable和Future创建线程

4)使用线程池创建线程

线程池的工作流程

1、如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务

2、如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列

3、如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立

刻运行这个任务

4、如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常

RejectExecutionException

你是怎么理解反射的?、

(1)反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自 身的情况为下一步的动作做准备。

Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method; 其中class代表的时类对象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法 对象。通过这四个对象我们可以粗略的看到一个类的各个组成部分。

(2)Java反射的作用:在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任 意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

(3)Java 反射机制提供功能在运行时判断任意一个对象所属的类。在运行时构造任意一个类的对象。

MySQL索引原理

MySQL的Innodb和MyISAM引擎索引都是通过B+树来实现的。

索引失效的场景

  1. 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 要想使用or,又 想让索引生效,只能将or条件中的每个列都加上索引
  2. 对于多列索引,不是使用的第一部分,则不会使用索引
  3. like查询以%开头
  4. 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
  5. 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
  6. 组合索引要遵循最左匹配原则

SQL优化

(1)尽量选择较小的列

(2)将where中用的比较频繁的字段建立索引

(3)select子句中避免使用‘*’

(4)避免在索引列上使用计算、not in 和<>等操作

(5)当只需要一行数据的时候使用limit 1

(6)保证单表数据不超过200W,适时分割表。针对查询较慢的语句,可以使用explain 来分析该语句具体 的执行情况。

(7)避免改变索引列的类型。

(8)选择最有效的表名顺序,from字句中写在最后的表是基础表,将被最先处理,在from子句中包含多个表 的情况下,你必须选择记录条数最少的表作为基础表。

(9)避免在索引列上面进行计算。

(10)尽量缩小子查询的结果

数据库分表操作

可以说使用Mycat或者ShardingSphere等中间件来做,具体怎么做就要结合具体的场景进行分析了。

如何进行表设计

  1. 数据库设计的实用原则是:
  1. 在数据冗余和处理速度之间找到合适的平衡点。原则是相对的,不是绝对的。

  2. 做表设计,读懂需求就对了。先不要管性能,先实现需求。表设计好了,写SQL的时候再考虑该合 并,合并,该拆分,拆分。另外最关键的就是搞清楚一对一还是一对多。

  1. 表设计范式:
  1. 保证每列的原子性,不可分解,意思表达要清楚,不能含糊,高度概括字段的含义,能用一个字段表 达清楚的绝不使用第二个字段,可以用两个字段表达清楚的绝不使用一个字段

  2. 表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的 设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。 例如:表内有商品单价和数量字段,我们设计的时候加上了一个金额的字段,这虽然违背了数据库设计的 第三范式,但以查询统计,这能大大提高查询的速度,这就是空间换时间的做法。

3、表关系设计:

  1. 最好做好静态表和动态表的分离。这里解释一下静态表和动态表的含义,静态表:存储着一些固定不 变的资源,比如城市/地区名/国家。动态表:一些频繁修改的表

  2. 不要有null值,有null值的话,数据库在进行索引的时候查询的时间更久,从而浪费更多的时间! 建议可以为null的值转换成not null default

  3. 2张表的多对多的表关系,最好设计成3张表,即增加一张中间表,之前的两张表和中间表的关系是一 对多的关系。

  4. 建表的时候,字段长度尽量要比实际业务的字段大3-5个字段左右(考虑到合理性和伸缩性),最好 是2的n次方幂值。不能建比实际业务太大的字段长度,这是因为如果字段长度过大,在进行查询的时候索引在 B-Tree树上遍历会越耗费时间,从而查询的时间会越久;但是绝对不能建小,否则mysql数据会报错,程序会

抛出异常;

  1. 对于频繁修改的字段(一般是指状态类字段)最好用独立的数字或者单个字母去表示,不用使用汉字 或者英文

  2. 数据库不要存储任何资源文件,比如照片/视频/网站等,可以用文件路径/外链用来代替,这样可以 在程序中通过路径,链接等来进行索引

  3. 关系映射:多对一或者一对多的关系,关联一张表最好通过id去建立关系,而不是去做重复数据,这 样做最大的好处就是中间的关系表比较清楚明白。

  4. 通过单一字段表示该行记录是否可用,通过一个单一字段去控制表是否可用,比如通常起名为 isVaild,预制的含义为0为有效,1为无效,这样便于以后我们去剔除数据或者重整数据,使其成为boolean 性质的数据 更加便于我们去操控。

  5. 预留备用字段:在设计一张表的时候应该预制2到3个空白字段,用于以后的扩展,因为你也不是确定 这张表以后不会扩展。

4、主键的使用:

  1. 主键不要与业务逻辑有所关联,最好是毫无意义的一串独立不重复的数字,常见的比如UUID或者将主 键设置为Auto_increment;

  2. 主键:主键可以是一无物理意义的数字串, 由程序自动加1来实现。也可以是有物理意义的字段名或 字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空 间大,而且速度也慢。

  3. 一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的外键, 所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;

5、提高数据库运行效率的办法 在系统硬件和系统软件条件确定的情况下,提高数据库系统的运行效率的办法是:

  1. 在数据库物理设计时,降低范式,增加冗余,少用触发器, 多用存储过程。

  2. 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以文件 系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。

  3. 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表 主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过八十个,则垂 直分割该表,将原来的一个表分解为两个表。

  4. 对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。

  5. 在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。

  6. 如果进行更新表的数据量较大,例如,更新的字段的值,需要重新从子表查询,且是全表查询,