类的加载
类的加载器机制: 我们编写的java文件都是.java为后缀的,编译器把.java转化成.class文件,类的加载机制就是将.class中的二进制数据存到内存中,并对数据进行校验,解释,初始化。然后在方法区存放相对应的元数据,在堆中创建相对应的class对象
类加载的三个阶段:加载,连接(验证,准备,解释),初始化
双亲委派机制:如图
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的流程
如果是红黑树就直接插入树中,如果是链表会判断链表长度是否大于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+树来实现的。
索引失效的场景
- 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 要想使用or,又 想让索引生效,只能将or条件中的每个列都加上索引
- 对于多列索引,不是使用的第一部分,则不会使用索引
- like查询以%开头
- 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引
- 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
- 组合索引要遵循最左匹配原则
SQL优化
(1)尽量选择较小的列
(2)将where中用的比较频繁的字段建立索引
(3)select子句中避免使用‘*’
(4)避免在索引列上使用计算、not in 和<>等操作
(5)当只需要一行数据的时候使用limit 1
(6)保证单表数据不超过200W,适时分割表。针对查询较慢的语句,可以使用explain 来分析该语句具体 的执行情况。
(7)避免改变索引列的类型。
(8)选择最有效的表名顺序,from字句中写在最后的表是基础表,将被最先处理,在from子句中包含多个表 的情况下,你必须选择记录条数最少的表作为基础表。
(9)避免在索引列上面进行计算。
(10)尽量缩小子查询的结果
数据库分表操作
可以说使用Mycat或者ShardingSphere等中间件来做,具体怎么做就要结合具体的场景进行分析了。
如何进行表设计
- 数据库设计的实用原则是:
-
在数据冗余和处理速度之间找到合适的平衡点。原则是相对的,不是绝对的。
-
做表设计,读懂需求就对了。先不要管性能,先实现需求。表设计好了,写SQL的时候再考虑该合 并,合并,该拆分,拆分。另外最关键的就是搞清楚一对一还是一对多。
- 表设计范式:
-
保证每列的原子性,不可分解,意思表达要清楚,不能含糊,高度概括字段的含义,能用一个字段表 达清楚的绝不使用第二个字段,可以用两个字段表达清楚的绝不使用一个字段
-
表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是最好的 设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的。 例如:表内有商品单价和数量字段,我们设计的时候加上了一个金额的字段,这虽然违背了数据库设计的 第三范式,但以查询统计,这能大大提高查询的速度,这就是空间换时间的做法。
3、表关系设计:
-
最好做好静态表和动态表的分离。这里解释一下静态表和动态表的含义,静态表:存储着一些固定不 变的资源,比如城市/地区名/国家。动态表:一些频繁修改的表
-
不要有null值,有null值的话,数据库在进行索引的时候查询的时间更久,从而浪费更多的时间! 建议可以为null的值转换成not null default
-
2张表的多对多的表关系,最好设计成3张表,即增加一张中间表,之前的两张表和中间表的关系是一 对多的关系。
-
建表的时候,字段长度尽量要比实际业务的字段大3-5个字段左右(考虑到合理性和伸缩性),最好 是2的n次方幂值。不能建比实际业务太大的字段长度,这是因为如果字段长度过大,在进行查询的时候索引在 B-Tree树上遍历会越耗费时间,从而查询的时间会越久;但是绝对不能建小,否则mysql数据会报错,程序会
抛出异常;
-
对于频繁修改的字段(一般是指状态类字段)最好用独立的数字或者单个字母去表示,不用使用汉字 或者英文
-
数据库不要存储任何资源文件,比如照片/视频/网站等,可以用文件路径/外链用来代替,这样可以 在程序中通过路径,链接等来进行索引
-
关系映射:多对一或者一对多的关系,关联一张表最好通过id去建立关系,而不是去做重复数据,这 样做最大的好处就是中间的关系表比较清楚明白。
-
通过单一字段表示该行记录是否可用,通过一个单一字段去控制表是否可用,比如通常起名为 isVaild,预制的含义为0为有效,1为无效,这样便于以后我们去剔除数据或者重整数据,使其成为boolean 性质的数据 更加便于我们去操控。
-
预留备用字段:在设计一张表的时候应该预制2到3个空白字段,用于以后的扩展,因为你也不是确定 这张表以后不会扩展。
4、主键的使用:
-
主键不要与业务逻辑有所关联,最好是毫无意义的一串独立不重复的数字,常见的比如UUID或者将主 键设置为Auto_increment;
-
主键:主键可以是一无物理意义的数字串, 由程序自动加1来实现。也可以是有物理意义的字段名或 字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个数不要太多,多了不但索引占用空 间大,而且速度也慢。
-
一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的外键, 所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;
5、提高数据库运行效率的办法 在系统硬件和系统软件条件确定的情况下,提高数据库系统的运行效率的办法是:
-
在数据库物理设计时,降低范式,增加冗余,少用触发器, 多用存储过程。
-
当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以文件 系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。
-
发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表 主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过八十个,则垂 直分割该表,将原来的一个表分解为两个表。
-
对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。
-
在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。
-
如果进行更新表的数据量较大,例如,更新的字段的值,需要重新从子表查询,且是全表查询,