慢慢学习,慢慢成长
一、基础语法、关键字
1、static
- static 可用于修饰
变量
、方法
、静态块
、静态内部类
以及静态导包
。 - java中的static关键字不会影响到变量的变量或者方法的作用域
- 静态变量在类加载时初始化,不需要new对象,通过 “ 类名.”的方式调用(不建议“引用.”的方式访问)。
- 静态变量储存在方法区。
- static修饰类时只能修饰
内部类
2、final
- final可用于修饰
类
、方法
以及变量
修饰类时,该类不可被继承。
修饰方法时,该方法不可被重写。
修饰变量时,局部变量可随时赋值;成员变量需在类实例化前赋值,可通过直接赋值或构造方法赋值。注意以下两点:
1、修饰基础数据类型只可有一次赋值;
2、修饰对象数据类型,地址不变属性可变。
3、interface
接口可以理解为一种特殊的抽象类,但不能有构造器。
- 接口中成员变量默认为 public static final 修饰。
- 接口中方法默认为 public abstract 修饰。
- 接口中可以有默认方法,如 public default void function () {},抽象方法必须实现,默认方法可不实现。
4、abstract
- 抽象类一定要有构造方法,但不能被实例化。(猜测与类初始化有关,待考证。)
- 抽象方法必须被实现。
- abstract不能修饰变量、代码块、构造器。
- abstract不能修饰私有方法、静态方法、final方法、final类。
接口与抽象类的区别
1、抽象类速度更快:接口需要时间去寻找在类中实现的方法。
2、JDK8后,接口也可以有默认方法,升级接口时可避免影响已经实现此接口的类。
3、抽象类只能单继承,接口可以多实现。
5、volatile
-
实现原理:
观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令
lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
2)它会强制将对缓存的修改操作立即写入主存;
3)如果是写操作,它会导致其他CPU中对应的缓存行无效。 -
当程序在运行过程中,会将运算所需要的数据从
主内存
中拷贝一份到高速缓存
(私有的本地内存—Local Memory)中以便提高运算效率,但在多线程情况下会造成缓存一致性问题(通常称这种被多个线程访问的变量为共享变量
)。
-
volatile的两大特性:
1、 保证可见性(1)当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去。
(2)这个写操作会导致其他线程中的volatile变量缓存无效。2、 禁止指令重排序
(1)重排序操作不会对存在数据依赖关系的操作进行重排序。
比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运行时这两个操作不会被重排序。
(2)重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变
比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系, 所以可能会发生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。但是,想要保证线程安全要同时满足
原子性
、可见性
以及有序性
,所以volatile只适用于原子性操作中(如i++不是原子性操作,不能使用volatile,可用synchronize、AtomicInteger等)。
volatile 常用于多线程环境下的单次操作(单次读或者单次写)。 -
使用场景:
synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:
1)对变量的写操作不依赖于当前值。
2)该变量没有包含在具有其他变量的不变式中。