总结
总结
基础
- 修饰符范围
修饰符 | 范围 |
---|---|
private | 只允许本类访问 |
缺省(default) | 允许本类,同包访问 |
protected | 允许本类,同包,子类访问 |
public | 允许本类,同包,子类,全局访问 |
- 包装类型的缓存
Byte
,Short
,Integer
,Long
存在[-128,127]的缓存,Character
存在[0,127]的缓存,Boolean
直接返回True、False
- 自动拆箱装箱
1 | Integer i = 10 //等价于 Integer i = Integer.valueof(10) |
- 静态变量、成员变量、局部变量
静态变量:被static
修饰的成员变量,全局只有一份(指的内存分配),所有实例共享,只分配一次内存,类加载时分配
成员变量:成员变量如果被static
修饰则为静态变量属于类,反之属于实例,实例化时分配内存,存放在堆中,可以被public
,private
,static
等修饰符所修饰
局部变量:局部变量则存在于栈内存,局部变量不能被访问控制修饰符及 static
所修饰;但是,成员变量和局部变量都能被 final
所修饰。
- 重载、重写
重载发生在同一个类中(或者父类和子类之间),方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
区别点 | 重载方法 | 重写方法 |
---|---|---|
发生范围 | 同一个类 | 子类 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可修改 | 子类方法返回值类型应比父类方法返回值类型更小或相等 |
异常 | 可修改 | 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等; |
访问修饰符 | 可修改 | 一定不能做更严格的限制(可以降低限制) |
发生阶段 | 编译期 | 运行期 |
- 面向对象三大特征
封装:对象属性隐藏在对象内部,复杂封装对外提供简单入口
继承:不同对象之间公有特征抽取成父类,子类继承可自行扩展
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
多态:父类引用指向子类实例,程序运行期间才能确定调用的方法属于哪个实例
- 接口、抽象类
同:
- 不能之间实例化
- 都包含抽象方法(方法默认是
public abstract
),没有方法体,在子类中实现
异:
- 设计目的:接口是对类的行为约束,抽象类是代码复用,强调所属关系
- 类单继承,接口多实现
- 接口中的成员变量都是常量(默认省略
public static final
) - 抽象类中可以有非抽象方法
- 深浅拷贝
区别在于拷贝目标内部的引用类型,浅拷贝直接复制内存地址,深拷贝重新开辟空间
引用拷贝:多个引用指向堆中同一对象
- ==和equals()
==
对于基本类型和引用类型的作用效果是不同的:
对于基本数据类型来说,==
比较的是值。
对于引用数据类型来说,==
比较的是对象的内存地址。
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
equals()
不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()
方法存在于Object
类中,而Object
类是所有类的直接或间接父类,因此所有的类都有equals()
方法。
Object
类 equals()
方法:
1 | public boolean equals(Object obj) { |
equals()
方法存在两种使用情况:
类没有重写 equals()
方法:通过equals()
比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object
类equals()
方法。
类重写了 equals()
方法:一般我们都重写 equals()
方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
- 为什么重写equals()时必须重写hashcode()
当你把对象加入
HashSet
时,HashSet
会先计算对象的hashCode
值来判断对象加入的位置,同时也会与其他已经加入的对象的hashCode
值作比较,如果没有相符的hashCode
,HashSet
会假设对象没有重复出现。但是如果发现有相同hashCode
值的对象,这时会调用equals()
方法来检查hashCode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了equals
的次数,相应就大大提高了执行速度。
总结:
equals
方法判断两个对象是相等的,那这两个对象的hashCode
值也要相等。- 两个对象有相同的
hashCode
值,他们也不一定是相等的(哈希碰撞)。
String StringBuffer StringBuilder
的区别
String
是不可变的:底层是私有且被final修饰的char[],不会被继承
String
的+运算符实际是使用StringBuffer
的append(),但是如果在循环中使用+就会创建多个StringBuffer
对象
String
中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder
是 StringBuilder
与 StringBuffer
的公共父类,定义了一些字符串的基本操作,如 expandCapacity
、append
、insert
、indexOf
等公共方法。StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
- 字符串常量池
字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
1 | // 在堆中创建字符串对象”ab“ |
- 代理模式
1.静态代理
静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
1.定义发送短信的接口
1 | public interface SmsService { |
2.实现发送短信的接口
1 | public class SmsServiceImpl implements SmsService { |
3.创建代理类并同样实现发送短信的接口
1 | public class SmsProxy implements SmsService { |
4.实际使用
1 | public class Main { |
2.动态代理
2.1JDK动态代理
代理接口
1.定义发送短信的接口
1 | public interface SmsService { |
2.实现发送短信的接口
1 | public class SmsServiceImpl implements SmsService { |
3.定义一个 JDK 动态代理类
1 | public class DebugInvocationHandler implements InvocationHandler{ |
4.获取代理对象的工厂类
1 | public class JdkProxyFactory { |
5.实际使用
1 | SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl()); |
2.2CGLIB动态代理
代理类
1.添加依赖
1 | <dependency> |
2.编写基础类
1 | public class AliSmsService { |
3.自定义 MethodInterceptor
(方法拦截器)
1 | public class DebugMethodInterceptor implements MethodInterceptor { |
4.获取代理对象
1 | public class CglibProxyFactory { |
5.使用
1 | AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class); |
数据库
索引
提高数据检索效率,降低数据库IO成本,通过索引对数据排序,减少CPU负载
索引底层使用B+树,优点:
- 阶数更多,路径更短
- 磁盘读写代价低,非叶子节点只存储指针,只有叶子节点存储数据
- 叶子节点是双向链表,便于扫库和区间查询
- B树和B+树的区别
- B树叶子和非叶子节点都会存放数据,B+树仅叶子节点存储数据,查询效率更高
- B+树的叶子是双向链表便于区间查询
- 聚簇索引和非聚簇索引
聚簇索引是指叶子节点存放一整行数据,一般是主键不用我们创建,可以根据主键key进行查询
非聚簇索引是指叶子节点除了索引字段的数据不存放数据,只存放主键key
当我们查询非索引字段的数据,通过非叶子节点的key找到叶子节点,再通过主键key去聚簇索引获取数据,这个过程叫做回表查询
- 覆盖索引
需要查询的字段存在索引,可直接命中,不需要回表查询
- 索引创建原则
- 表中数据超过10万
- 索引字段查询频繁,比如作为查询条件,排序字段
- 创建索引一般使用聚合索引,减少回表查询频率
- 控制索引数量
- 最左匹配原则
索引abc_index:(a,b,c)是a,b,c三个字段的联合索引,下列sql执行时都无法命中索引abc_index;
1 | select * from table where c = '1'; |
以下三种情况却会走索引:
1 | select * from table where a = '1'; |
如果查询条件与索引最左侧的字段相匹配,会使用索引一直向右匹配查询,直到遇到范围查询
- 索引失效
- 没有遵循最左匹配原则
- 模糊查询,如果 % 号在前面也会导致索引失效
- 在添加索引的字段上进行了运算操作或者类型转换也都会导致索引失效
- 使用联合索引,中间使用了范围查询,右边的条件索引也会失效
SQL
优化
- 避免索引失效
- 不使用
select *
指明字段 - 遵循最左匹配原则
- 关联查询尽量使用
inner join
,少使用left join
right join
,必须使用时使用小表作为驱动
- MYSQL日志
binlog
归档日志: 记录所有涉及更新数据的逻辑操作,MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性redolog
事务日志: 恢复数据,保持数据持久性,记录数据页的物理修改undolog
回滚日志: 记录逻辑日志,当事务回滚,通过逆操作恢复数据
SQL基础
DISTINCT
去重关键字ORDER BY
先排序的字段放在前,后排序的字段放在后字段类型:
- 整形:
TinyInt int bigInt
- 浮点:
float double decimal
- 字符:
char varchar text
- 整形:
UNSIGNED
表示无符号,可以将数值正值上限提高一倍char
和varchar
的区别char
是定长字符串,varchar
是变长字符串char
没有使用完空间,补满空格,检索时去掉空格varchar
用1到2个字节记录字符串长度
Boolean
类型使用TinyInt(1)
存储引擎
默认使用
InnoDB
,只有它支持事务MyIsam(5.5之前)
和InnoDB
的区别前者支持表级锁,后者支持行级锁
前者不支持事务,后者支持事务
前者不支持外键,后者支持外键
Mysql
事务(逻辑上的一组操作,要么都执行,要么都不执行)事务的四性(ACID)
A:原子性,最小的执行单元,不允许分割,成功一起,失败一起
C:一致性,执行事务前后数据一致
I:隔离性,并发访问数据库时事务是隔离的
D:持久性,一个事务提交后。对数据库的影响是持久的
ADI是手段,保证了C一致性
事务的隔离级别
并发导致的问题:
1.脏读:另一事务读取一个事务修改并未提交的数据,之后这个事务回滚了
2.丢失修改:事务1,2执行相同操作丢失一次修改
3.不可重复读:事务1修改A=20 =》A=19,2事务在修改前后读到了A=20和A=19两次不同的数据
4.幻读:事务1读表,事务2插入一行数据,之后事务1进行再次查询多出了数据叫幻读
四种隔离级别
读未提交:导致脏读,幻读,不可重复读
读已提交:导致幻读,不可重复读
可重复读:导致幻读
可串行化:事务依次执行,隔离性好,存在性能问题