在讲继承的时候我们就知道父类的私有属性和构造方法不能被继承,所以constructor也不能被重写,但是可以被重载,所以可以看到一个类中有多个构造函数的情况。
重载发生在同一类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类,如果父类的方法访问修饰符为private则子类就不能重写该方法。
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head first java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
如果两个对象相等,则hashcode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashcode值,它们也不一定是相等的
因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
标识符的第一个字符必须是字母,下划线,$
final: 被final修饰的变量不可变(引用不可变,即它只能指向初始时指向的那个对象,而不关心对象内容的变化),被final修饰的变量必须初始化
final方法: 当一个方法修饰为final,该方法不允许被任何子类重写,但子类仍然可以用这个方法
final参数: 这个参数在这个函数内不允许被修改
final类: 此类不能被继承,所有方法不能被重写,但是可以被重载。
一个类不能既被声明为abstract,又被声明为final。abstract抽象类不能被实例化,也就是不能被new
finally: 作为异常的一部分,它只能用在try/catch中,并且附带一个语句块,表示这段语句最终一定被执行,经常用在需要释放资源的情况下。
finalize: 在垃圾回收器执行时会调用被回收对象的finalize()方法。
1.通过static关键字达到全局的效果;实现单例模式
2.static的方法是类的方法,不需要创建对象就可以被调用;
3.不能在成员变量内部定义static变量
4.static方法中不能使用this和super关键字
5.变量用static final修饰,表示一旦赋值,就不可以修改
volatile被设计来修饰不同线程访问和修改的变量。被volatile定义的变量,系统每次用到它时都是直接从对应的内存中提取,而不会利用缓存。
1字节:byte boolean
2字节:char short
4字节: int float
8字节:long double
封装类:由于java是面向对象的,而基本类型不具有对象的性质,为了让其具有对象的的特征,将其封装。在ArrayList,HashMap中用到。
所有基本类型的包装类都是不可变类,例如Integer,Float。此外,String也是不可变类
4.7Java IO流的实现机制是什么?
- 流分为字节流和字符流,其中字节流继承于InputStream和OutputStream,字符流继承于Reader和Writer。流的主要作用主要是为了改善程序性能并且使用方便
- 字节流(8bit)和字符流(16bit)区别:字节流在处理输入输出时不会用到缓存,而字符流用到了缓存。
- Java IO类采用了装饰器模式,好处在于可以在运行时动态地为对象添加一些额外的指责,很灵活
- Java IO(非阻塞IO),与传统的Socket(套接字)数据交换的方式相比,在处理大量并发请求时,使用NIO要比使用Socket效率高
new String("abc")创建了几个对象? 一个或者两个,如果常量池中原来有abc,那么只创建一个对象;如果常量池中原来没有字符串abc,那么创建两个对象。
==:比较变量对应的内存中存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==;对于指向对象类型的变量,如果要比较两个对象是否指向同一块存储空间,用==。
equals:对于对象类型的变量,要比较两个对象的内容是否相等,用equals
如果一个类没有自己定义equals()方法,它默认使用从Object类继承的==,此时equals和==是一样的效果,如果编写的类希望比较改类创建的两个实例对象的内容是否相等,那么必须覆盖equals方法。
length针对数组,length()针对字符串
当程序进入try语句之前出现异常,会直接结束,不会执行finally块中的代码;当程序在try块中强制退出时也不会去执行finally 块中的代码
异常:包括Error(不可恢复)和Exception(可恢复,编译器可捕获),它们的父类是Throwable
- 运行时异常和普通异常区别:
1.检查异常:发生在编译阶段,java编译器强制程序去捕获此类型的异常
2.运行时异常:编译器没有强制对其进行捕获并处理
1.java中有几种类型的流?
常见的流有两种,字节流(8bit)和字符流(16bit)。 字节流继承于InputStream与OutputStream,字符流继承于Reader和Writer。流的主要作用是改善程序性能和使用方便。
套接字:网络上两个程序通过一个双向的通信连接实现数据交换,这个双向链路的一端称为一个Socket。
Socket可以分为两种类型:面向连接的Socket通信协议(TCP,传输控制协议)
面向无连接的Socket协议(UDP,用户数据报协议)
1.装载。根据查找路径找到对应的.class文件,然后导入。
2.链接。链接又分为3个小步骤:
(1)检查。检查待加载的class文件的正确性。
(2)准备。给类中的静态变量分配存储空间。
(3)解析。将符号引用转换成直接引用(这一步是可选的)。
3.初始化。对静态变量和静态代码块执行初始化工作。
1.在堆中申请的空间没有被释放;
2.对象已不再被使用,但还仍然在内存中保留着。
垃圾回收机制的引入可以有效地解决第一种情况;而对于第二种情况,垃圾回收机制则无法保证不再使用的对象会被释放。
1.栈内存主要用来存放基本数据类型和引用变量。每当有函数调用时,都会通过压栈方式 创建新的栈帧,每当函数调用结束后都会通过弹栈的方式释放栈。
2.堆内存是用来存放运行时创建的对象。一般来讲,通过new关键字创建出来的对象都存放在堆内存中。
在堆中产生一个数组或对象后,可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。 P132
1.均为可伸缩数组,即可以动态改变长度的数组。
2.ArrayList和Vector都是基于存储元素的Object[] array来实现的,它们会在内存中开辟一块连续的空间来存储,由于数据存储是连续的,因此,它们支持下标来访问元素,索引速度块,但在插入元素时须移动容器中元素,插入慢。扩容时,Vector默认扩充为原来2倍,ArrayList扩充为原来1.5倍。 这两者最大的区别是synchronization的使用,ArrayList不同步,而Vector的绝大多数方法是同步的,所以Vector线程安全,ArrayList不是线程安全。正是由于Vector提供线程安全机制,其性能逊色于ArrayList。
3 . LinkedList采用双向链表实现,对数据的索引需要从列表头开始遍历,因此随机访问效率低,但是插入元素时不需要对数据进行移动,因此插入效率高。
在实际使用时,对数据主要操作为索引或者在集合末端进行增加,删除时,使用ArrayList或Vector效率高;当对数据的操作主要为为制定位置插入和删除时,使用LinkedList;当在多线程中使用容器时,选用Vector安全。
1.HashMap是Hashtable的轻量级实现,都完成了Map接口。HashMap允许一条空(null)键值(key),而HashTable不允许。
2.Hashtable继承自Dictionary类,而HashMap是java1.2引进的Map interface的一个实现。
3.Hashtable线程安全,而HashMap不支持线程的同步,所以是非线程安全的。在多个线程访问Hashtable时,不需要开发人员对它进行同步,而对于HashMap,开发人员必须提供额外的同步机制。
1.如果向根据对象的相关属性来自定义对象是否相等的逻辑,此时需要重写equals()方法,一旦重写了equals(),那么就必须重写hashCode()方法。
2.当自定义类的多项作为HashMap(Hashtable)的key时,最好把这个类设计为不可变类。
3.从HashMap的工作原理可以看出,如果两个对象相等,那么这两个对象有相同的hashCode,反之则不成立。
1. Collection:集合接口。 提供了对集合对象进行基本操作的通用接口方法。 实现该接口的类有List和Set, 该接口的设计目标是为各种具体的集合提供最大化的统一的操作方式。
2.Colleactions: 针对集合类的包装类, 提供了一系列静态方法以实现对各种集合的搜索,排序,线性安全化等操作。 Coolections类不能实例化,如同一个工具类,服务于Collection框架。
线程:指程序在执行过程中,能够执行程序代码的一个执行单元
进程:指一段正在执行的程序
线程又被称为轻量级进程,它是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段,数据段,堆空间)及一些进程的资源,但是各个线程拥有自己的栈空间。
1. 继承Thread类,重写run()方法: Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且启动线程的唯一方法是通过Thread类的start()方法。
class MyThread extends Thread{ //继承Thread类
public void run(){
... //重写run()方法
}
}
public class Test{
public static void main(String[] args){
Mythread thread = new Mythread();
thread.start(); //开启线程
}
}
2. 实现Runnable接口,并实现该接口的run()方法: 自定义类并实现Runnable接口,实现run()方法 -----> 创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象 --------> 调用Thread的start()方法。
class MyThread implements Runnable{ //实现Runnable接口
public void run(){
... //实现run()方法
}
}
public class Test{
public static void main(String[] args){
MyThread thread = new MyThread();
Thread t = new Thread(thread);
t.start(); //开启线程
}
}
3. 实现Callable接口,重写call()方法
public class CallableAndFuture{
public static class CallableTest implement Callable{ //实现Callable接口
public String call() throws Exception{
... //重写call()方法
}
}
public static void main(String[] args){
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future future = threadPool.submit(new CallableTest());//启动线程
try{
...
System.out.print(future.get()); //等待线程结束,并获取返回结果
}catch(Exeception e){
e.printStackTrace();
}
}
}
start():启动一个线程,此时该线程属于就绪状态,而非运行状态,就意味着整个线程可以被JVM来调度。
run():在调度过程中,JVM通过调用线程类的run()方法来完成实际的操作,当run()方法结束后,此线程就会终止。
1. synchronized()关键字: 该锁表明对象在任何时候只允许被一个线程拥有,当一个线程调用对象的一段synchronized代码时,需要先获得这个锁,然后去执行响应的代码,执行结束后,释放锁。
2. wait()方法与notify()方法: 在synhronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用notify()或者notifyAll()通知正在等待的其他线程。
3. Lock:
1. 原理不同:sleep()是Thread类的静态方法,它使此线程暂时执行一段时间,而把执行机会让给其他线程,等计时时间一到,此线程会自动苏醒。 wait()使Object类的方法,用于线程间的通信,这个方法使当前拥有该对象锁的进程等待,直到其他线程调用notify()方法。
2. 对锁的处理机制不同: sleep()方法不会释放锁; wait()线程会释放掉它所占用的锁,从而使线程所在对象中的其他synchronized数据可被别的线程调用。
3. 使用区域不同: wait()必须放在同步控制方法或同步语句块中使用,sleep()可在任何地方使用。
stop()和suspend()方法,用Thread.stop()来终止线程时,它会释放已经锁定的所有监视资源。suspend()方法容易造成死锁。所以一般采用的方法是让线程自行结束进入Dead状态。
1.用法: synchronized既可以加在方法上,也可以加在特定代码块中; 而Lock需要显式地指定起始位置和终止位置。
2. 性能: 在资源不是很激烈的情况下,synchronized性能优于ReetrantLock, 在资源竞争很激烈的情况下,synchronized性能下降很快,而ReetrantLock性能保持不变。
3.机制: synchronized,获得多个锁时,必须以相反顺序释放,自动解锁,不会引文出现异常引发死锁; Lock,手动解锁,必须在finally块中释放,否则引起死锁问题。
join()方法的作用是让调用该方法的线程在执行完run()方法后,再执行join方法后面的代码。简单来说,就是将两个线程合并,实现同步功能。
1. 完成与服务器端的交互
2. 完成HTML的解析,从而实现把用户需要查看的资源信息以直观的形式展示出来
1. 用户通过浏览器输入链接地址来请求所需的资源
2. 浏览器接受用户的请求,并把该请求组装成指定的格式发送给服务器端,客户端与服务器端通过HTTP来完成具体的交互。请求的数据流中包括:HTTP请求方法,请求的网址URL, 参数
3. 服务器收到客户端发来的请求,并查找用户所需要的资源
4. 服务器找到用户请求的资源后,把该资源返回给客户端
5.服务器通过把响应消息组装成特定的消息格式后返回给客户端,这个过程通过HTTP来完成。响应的数据流主要包括:状态编码,Comment-type,响应消息的内容
1. cookie采用的是在客户端保持状态的方案, 数据存放在客户的浏览器上; session机制采用的是在服务器端保持状态的方案,数据放在服务器上。
2.cookie安全性不够 由于cookie信息存放在客户端,其他人可以很容易地得到存放在本地的cookie,并进行cookie欺骗;而session信息存放在服务器端,比较安全。
3.cookie性能更高 由于session会在一定时间内保存在服务器上,因此当访问量增多时,会降低服务器的性能。
4.单个cookie保存的数据不能超过4KB,很多浏览器都限制一个站点最多保存20个cookie;而session不存在此问题。
select | 选择符合条件的记录 | select* from table where 条件语句 |
insert | 插入一条记录 | insert into table (字段)values (值) |
update | 更新 | update table set 字段名=字段值 where 条件表达式 |
delete | 删除记录 | delete from table where 条件表达式 |
create | 数据表的建立 | create table tablename 字段 |
drop | 数据表的删除 | drop table tablename |
grant | 为用户授予系统权限 | |
revoke | 收回系统权限 |
|
EXPLAIN | 用于描述表的细节 |
CONCAT | Mysql 数据库可以使用CONCAT或者CONCAT_WS两种函数进行拼接 |
insert ignore into 表名 values(...) | 批量插入数据,如果数据已经存在,请忽略 |
create unique index 索引名 on 表名(字段) | 建立唯一索引 |
force index | MySQL中,使用 FORCE INDEX 语句进行强制索引查询 |
alter 表名 ADD 字段 | 向已存在的表插入新字段 |
create trigger | 1、用 CREATE TRIGGER 语句构造触发器,用 BEFORE或AFTER 来指定在执行后面的SQL语句之前或之后来触发TRIGGER 2、触发器执行的内容写出 BEGIN与END 之间 3、可以使用 NEW与OLD 关键字访问触发后或触发前的employees_test表单记录 |
replace | replace(字段,“需要替换的值”,“替换后的值”) |
修改表名 | alter table 表名 rename 新表名 |
添加外键 | alter table 表名 add foreign key .. .references. .. |
求交集 | intersect |
substr(X,Y) | 其中X是要截取的字符串。Y是字符串的起始位置(注意第一个字符的位置为1,而不为0),取值范围是±(1~length(X)),当Y等于length(X)时,则截取最后一个字符;当Y等于负整数-n时,则从倒数第n个字符处截取。 |
group_concat(要连接的字段 SEPARATOR 连接时使用的符号 ) |
集合函数,起连接作用,此函数必须与group by配合使用 |
limit m,n | 从第m+1条开始取n条 |
CREATE TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);
对于表来说,type 字段永远是 ‘table’,name 字段永远是表的名字。所以,要获得数据库中所有表的列表, 使用下列SELECT语句:
SELECT name FROM sqlite_master
WHERE type=’table’
ORDER BY name;
对于索引,type 等于 ‘index’, name 则是索引的名字,tbl_name 是该索引所属的表的名字。
inner join : 两边表同时有对应的数据,即任意一边缺失数据就不显示
left join:会读取左边数据表的全部数据,即便右边表无对应数据
right join:会读取右边数据表的全部数据,即便左边表无对应数据
group by 语句用来与聚合函数联合使用,having语句的存在弥补了where关键字不能与聚合函数联合使用的不足
select ...
from ...as... inner join ...as...
on ...
not in | 不在什么里面 |
MAX() as ... | 选最大 |
distinct | 不重复 |
1.用delete操作后,被删除的数据占用的内存空间还在,可以恢复,delete执行的过程是每次从表中删除一行数据,同时将地喊出的操作以日志的形式保存,一遍将来进行回滚操作。 truncate操作删除数据后,被删除的数据会立即释放占用的存储空间,一旦执行不能被回滚,被删除的数据不能恢复。
2.truncate速度快于delete
内链接只显示符合连接条件的记录,外连接除了显示符合连接条件的记录外,还显示表中的记录。外连接有左外连接,右外连接,全连接
union在进行表求并集后会去掉重复的元素,回对所产生的结果集进行排序运算; union all只是简单的将两个结果集合合并后旧返回结果。
单例模式: 保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。
redo log是物理日志,记录的是在某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如给ID=2这一行的c字段加1
redo log循环写,空间固定会用完;binlog可追加写,不会覆盖以前的日志
大多数情况下优化器都能找到正确的索引,但偶尔还是会碰到原本可以执行的很快的SQL语句,执行速度却比预期的慢很多
不会丢失。虽然只更新内存,但是事务提交的时候,我们把change buffer的操作也记录到了redo log里了,所以崩溃恢复的时候,change buffer也能找回来。
要收缩一个表,只是delete掉表里面不用的数据的话,表文件的大小是不会变的,你还要通过alter table命令来重建表,才能达到表文件变小的目的。
重建表的两种实现方式:Online DDL的方式是可以考虑再业务低峰期使用的,而Mysql5.5及以前的版本,这个命令是会阻塞DML的。
每次事务都要提交redo log,如果设置太小,很快会被写满,这时候系统不得不停止所有更新,去推进checkpoint。磁盘压力很小,但是数据库出现间歇性的性能下跌。
可以用一个redis服务来保存这个表的总行数。表每插入一行redis计数加1,每被删除一行redis计数减1;这种方式下,读和更新的操作很快,但是缓存系统可能会丢失更新,即使redis正常工作,这个值还是逻辑上不精确的,因为在并发系统里,我们无法精确控制不同线程的执行时刻。
2. 在数据库保存计数:(把计数直接放到数据库里单独一张计数表)
解决了崩溃丢失的问题,InnoDB是支持崩溃恢复不丢数据的;解决了一致性视图的问题。用事务来确保计数准确,由于事务可以保证中间结果不被别的事务读到,因此修改数值和插入新纪录的顺序是不影响逻辑结果的。
在DDL期间,如果刚好有外部的DML在执行,这期间可能会引起一些新的空洞。将表t重建依次,插入一部分数据,但插入的这些数据用掉了一部分预留空间(再重建表的时候,InnoDB不会把整张表占满,每个页留了1/16给后续的更新用),这种情况下,再重建一次表t,就可能出现问题中的现象。
对于索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。在这个例子中,优化器放弃了树搜索功能,优化器可以选择遍历主键索引和选择遍历索引t_modified,优化器对比索引大小后发现,索引t_modified更小,遍历这个索引比遍历主键索引来的快,因此最终选择索引t_modified。
例子中,由于在t_modified字段加了month()函数操作,导致了全索引扫描。为了能够用上索引的快速定位能力,我们需要把sql语句改成基于字段本身的范围查询。
在mysql中,字符串和数字做比较的话,是将字符串转换成数字。
结果是:优化器会现在交易记录表tradelog上用主键索引查到id=2的行,但是没有用上交易详情表trade_detail上的tradeid索引,而是用主键id进行了全表扫描。
原因:两个表的字符集不同,一个是utf8,一个是utf8m64,所以做表连接查询的时候用不上关联字段的索引。
字符集utf8m64是utf8的超集,所以当这两个类型的字符串做比较的时候,mysql内部的操作是,先把utf8转换为utf8m64字符集,在做比较。-----> 字符集不同只是条件之一,连接过程中要求在被驱动表的索引字段上加函数操作,是直接导致对被驱动表做全表扫描的原因
小结:对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能
第一类---------查询长时间不返回 :这种情况大概率是表被锁住了,用show processlist命令分析原因。
1.等MDL锁----命令查看显示为Waiting for table metadata lock :出现这个状态表示的是,现在有一个线程正在表上请求或者持有MDL写锁,把select语句堵住了。这类问题的处理方式,就是找到谁持有MDL写锁,然后把它kill掉。
2.等flush---waiting for table flush :这个情况可能是有一个flush tables命令被别的语句堵住了,然后它又堵住了我们的select语句
3.等行锁---
第二类---查询慢:
1.由于字段上没有索引,因此查询语句只能走id主键顺序扫描
2.带lock in share mode的sql语句,是当前读,因此会直接读到最新的结果,所以速度很快;但是没有加事务的语句,是一致性读,需要依次执行undo log,所以慢。
幻读是指一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。幻读专指新插入的行
redo log的三种状态:
1.存在redo log buffer中,物理上是在MySQL进程内存中;
2.写到磁盘,但是没有持久化,物理上是在文件系统的page cache里面;
3.持久化到磁盘,对应的是hard disk;
1.两个场景让一个没有提交事务的redo log写到磁盘中:
1.redo log buffer占用的空间即将达到innodb_log_buffer_size一半的适合,后台线程会主动写盘;
2.并行的事务提交的适合,顺带将这个事务的redo log budder持久化到磁盘