个人认为:不存在引用传递,均是值传递
如下图所示代码:
对应的输出结果是:
值传递: 传的是值的副本
局部变量的作用域: 局部变量随着方法而产生,也随着方法而销毁
引用类型的修改: 修改的是堆中的数据值
String类型的存放: 存放于字符串常量池
,被final修饰
String类型的修改: 先判断字符串常量池中,有没有修改后的值
?有的话,直接指向原有的值;没有的话,再创建一个,指过去(其实就是修改了地址值
)
基础类型的包装类: 方法传参中,具有不可变性
解析代码:
main方法中: age变量的 值是20
personName变量的 值是地址值(“abc”在堆中的地址值)
str变量的 值是地址值(“abc”在堆中的地址值)
changeValue1方法中: age变量只是一个副本,原值是20,修改后是30 (基本类型)
但是在方法销毁后,这个age变量也被销毁,也就是这个副本被销毁
而main方法中的原件变量值并没有被修改
changeValue2方法中: personName变量只是一个副本,修改时是修改了地址值所指向堆中的数据值 (引用类型)
堆中的原值是“abc”,修改后是“xxx”,但是其地址值没变
但是在方法销毁后,这个personName变量也被销毁,也就是这个副本被销毁
而main方法中的原件变量值并没有被修改,还是原来的地址值,此时该地址值指向的堆中数据却被修改了
changeValue3方法中: str变量只是一个副本,修改时是修改了地址值
(String类型)
原值是“abc”在字符串常量池中的地址值,修改后是“xxx”在字符串常量池中的地址值
但是在方法销毁后,这个str变量也被销毁,也就是这个副本被销毁
而main方法中的原件变量值未被修改,还是那个地址值,此时该地址值指向字符串常量池中的数据未被修改
== : 可使用于基本类型,也能使用于引用类型
equals : 只能使用于引用类型
如图所示代码:
对应的结果为:
解析代码:
== : 比较的就是 栈中变量的值 ;如果是 基本类型 ,就是比较数据值
;如果是 引用类型 ,就是比较地址值
凡是 new出来的对象 ,栈中变量的值都是地址值
;而 不同的new对象 ,地址值是不一样的
,所以 ==
比较百分百是false
equals: 属于Object类的方法;Object类中对其的实现就是用了 ==
所以只要一个类 没有重写equals方法 ,那么就等同于 ==
String类, 重写了equals方法 ,而且是 比较数据值的hashCode值
String类,重写了hashCode方法,只要数据值一样,hashCode值就一样
所以String变量中,只要 数据值一样 ,用equals方法比较就肯定是true
HashSet : 根据hashCode值比较是否是同一个值
;new出来的对象,hashCode值都不一样,除非重写了hashCode方法
String类,重写了hashCode方法,只要数据值一样,hashCode值就一样
所以上述代码中,结果如图所示
在 jdk1.8 之前,字符串常量池在方法区中
;为了减小方法区内存溢出风险,在 jdk1.8 之后 就把常量池移到java 堆中
了
String s = "abc"
:创建的对象,存储在字符串常量池中;在创建字符串对象之前,会先在 常量池中检查是否存在 abc 对象 ;存在,则直接返回常量池中 abc 对象的引用
;不存在,会在常量池中创建该对象,并将常量池中该对象的引用返回给对象 s
简单来说,就是String s = ""
创建的局部变量 s,其值是字符串常量池的地址值
String s = new String("abc")
:实际上 abc
本身就是字符串常量池中的一个对象,在运行 new String()
时,把常量池中的字符串 abc 复制到堆中;因此该方式 会在堆和常量池中都创建 abc 字符串对象; 最后把 java 堆中对象的引用返回给 s
简单来说,就是String s = new String("")
创建的局部变量 s,其值是堆的地址值
调用 String 的 intern 方法 时,先 判断字符串常量池中是否已经有对应的字符串(通过 String 的 equals 方法
比较);有的话,则直接返回常量池中对应的地址值;没有的话,就先在常量池中创建,再返回常量池中对应的地址值
所以:对于任意两个字符串 s 和 t ,s.intern() == t.intern()
比较;仅当s.equals(t)
为true时,才为true
简单来说,就是String s = s.intern()
创建的局部变量 s,其值是字符串常量池的地址值
如图所示代码:
对应的结果为:
解析: 上述代码中,栈中局部变量:
s1的值是 字符串常量池 的地址值,s2的值是 堆 的地址值;所以两个地址值不一样
s1和s5的 字符串值都不一样 了,字符串常量池中的地址值肯定也不一样
s1和s6的 字符串值都不一样 了,字符串常量池中的地址值肯定也不一样
s1和s6.intern()的 字符串值都不一样 了,字符串常量池中的地址值肯定也不一样
s2的值是 堆 的地址值,s2.intern()的值是 字符串常量池 的地址值;所以两个地址值不一样
如图所示代码:
对应的结果为:
普通代码块: 在方法或语句中出现的{...}
;就称为普通代码块
;普通代码块可以多次执行
普通代码块的执行顺序: 两部分普通代码块,执行顺序是“先出先执行”
1)构造代码块的 概念
直接在类中定义且 没有用static修饰、没有名字 的{...}
代码块;称为构造代码块
;构造代码块可以 多次执行
2)构造代码块的 执行顺序
两部分构造代码块,执行顺序是“先出先执行”
3)构造代码块的 执行时机
构造代码块在每次创建对象时都会被调用
;是在构造方法方法中的隐式调用
,在隐式调用父类构造方法之后
4)构造代码块与构造方法的优先级
如图所示代码:
对应的结果为:
解析: 构造代码块优先于构造方法
1)静态代码块、构造代码块、构造方法的 优先级
静态代码块 > 构造代码块 > 构造方法
静态代码块只执行一次;构造代码块和构造方法可多次执行
2)无继承情况例子说明
如图所示代码:
对应的结果为:
解析:
(1)在运行CodeBlock03的 main方法前,生成CodeBlock03的模板,此时运行了CodeBlock03的静态代码块
(2)之后运行CodeBlock03的main方法
(3)调用Code的构造方法,需要 先生成Code的模板,才能调用构造方法;此时优先运行了Code的静态代码块
(4)运行完Code的静态代码块之后,需要先运行Code的构造代码块
(5)运行完Code的构造代码块之后,才能运行Code的构造方法
之后的调用,由于 静态代码块只执行一次 ,所以在原本的顺序下,踢出静态代码块即可
3)有继承情况例子说明
如图所示代码:
对应的结果为:
解析:
(1)运行TestStaticSeq的main方法
(2)调用子类的构造方法,需要 先生成父类的模板,才能生成子类的模板;此时优先运行了父类的静态代码块
(3)生成父类的模板后,要先生成子类的模板;才能运行子类的构造方法,此时优先运行了子类的静态代码块
(4)运行子类构造方法,先 调用父类构造方法(隐式);调用父类构造方法,需先运行父类构造代码块
(5)运行了父类构造代码块之后,才会运行父类的构造方法
(6)在子类构造方法中,调用完父类构造方法后(隐式),会调用子类的构造代码块
(7)运行完子类的构造代码块之后,才会运行子类的构造方法
之后的调用,由于 静态代码块只执行一次 ,所以在原本的顺序下,踢出静态代码块即可
4)构造方法的 隐式三步
在 调用一个类的构造方法时(无论是无参还是有参) ,都会有一个隐式三步
:
(1)调用父类的构造方法(无参调无参,有参调有参)
(2)给非静态成员变量赋予显示值
(3)调用自身的构造代码块
具体如下:下面代码中,1、2、3步就是隐式3步
;最后还有一个隐式 return 返回
注意: 隐式三步的第二步和第三步是不固定的,哪个写前面就先哪个
public class Father{
private int id = 007;
private String name = "零零发";
private int age = 98;
private float height = 1.78F;
public Teacher() {
// 1、super(); 调用父类的构造方法(无参调无参,有参调有参)
// 2、id = 007; 给非静态成员变量赋予显示值(在JVM内存程度上,真正的关联变量名和变量值)
.
.
.
// 3、{...} 调用自身的构造代码块
... // 自己手写的代码
// return; 隐式返回
}
进程和程序,看看哪个进程占比较高
%CPU:CPU使用占比
%MEM:内存使用占比
%Cpu(s):Cpu使用情况(在多核机器时,可通过连按6次1键;来查看每个CPU的使用情况
)
id:idle,空闲率(越大越好)
load average:负载的平均数;如果有三个数,就是 1分钟、5分钟、15分钟的系统平均负载量
三个数相加,除以3,乘以百分百;如果高于60%,说明系统负载严重
按键 q,退出
一样有 load average
free: 按字节展示,自己的换算
free -g: 以 g 为单位展示,没小数;所以误差比较大
所以:建议使用free -m
df: 按字节展示,自己的换算
所以:建议使用df -h
看头的进程数,看尾的CPU使用情况
看最后的 %util ,说明磁盘IO高,可能需要进行SQL语句优化
Spring 的 bean 有 4种 作用域,分别是:singleton、prototype、request、session
1、singleton: 单例的作用域,默认值;在 初始化 IOC 容器时 创建对应的唯一的bean
,之后再也不创建
2、prototype: 原型的作用域;这里的原型指的是每一个 getBean() 方法
都会创建自己的bean
3、request: 每一次请求都会实例化一个bean
4、session: 每一个会话都会实例化一个bean
通过 scope
属性进行指定
1)传播行为的理解: 当一个方法调用另一个方法时,两个方法均有事务时,对多个事务的处理机制
2)传播行为的类型: 有 7种 传播行为,但是 常用的就两种 ;required、requires_new
3)传播行为的设置: 通过 propagation 属性进行指定
1)脏读: 线程A在修改数据库,线程B读取了修改后的值进行后续操作;线程A却发生了 回滚 ,线程B读取的数据就是脏读
2)不可重复读: 线程A读取了一次数据,线程B修改了这个数据,线程A再次读取时,出现两次读取不一致
3)幻读: 线程A读取了数据行,线程B增加或删除了n行,线程A再次读取数据行时,出现两次读取行数不一致
1)隔离级别的理解: 针对 第2点的三种问题 的处理级别
2)隔离级别的类型: 有 4种 传播行为
(1)read uncommitted:读未提交
(2)read committed:读已提交
(3)repeatable read:可重复读
(4)serializable:串行化
由于 隔离级别越高 ,数据一致性越好,但并发性越差
所以 常用的就两种 ;read committed、repeatable read
3)隔离级别的设置: 通过 isolation 属性进行指定
在 指定的时间间隔 内,将内存中的数据集快照写入磁盘
恢复时,直接将快照文件读到内存中
优点: 节省磁盘空间;恢复速度快
缺点: 数据庞大时比较消耗性能;有可能会丢失最后一次快照之后的数据
以日志的方式来记录每个写操作,只追加文件不改写文件
Redis启动之初会读取该文件重新构建数据;也就是Redis重启时,根据日志文件内容从前至后执行一次
,已完成恢复工作
优点: 备份机制更稳健;丢失数据概率更低;可以处理误操作
缺点: 比RDB占用更多的磁盘空间;恢复速度慢;每次读写都同步的话,有一定性能压力;存在个别bug,没办法恢复
1)主键自动创建唯一索引
2)频繁作为查询条件的字段
3)与其他表关联的字段(外键关系)
4)查询中需要统计或者分组的字段
5)查询中需要排序的字段
6)单键索引和组合索引,组合索引的性价比更高
1)表记录太少
2)经常增删改的表或者字段
3)查询条件中用不到的字段
4)过滤性不好的字段
5)标记清除压缩算法: 主要是 full gc
在使用; 3)和4)的结合 ;先标记、后清除;等到内存碎片较多时,进行压缩