一、static关键字
1、介绍:
译为静态,可以作用在方法,成员变量,代码块上,内部类。
随着类的加载而加载 ,优先于对象存在,因此静态方法不能调用非静态方法、实例变量、非静态内部类,反过来是可以的。
2、作用:
作用在方法上:
访问权限允许时,可不创建对象,直接被类调用。
public class User{
...
public static void print(){
System.out.println("打印信息!");
}
...
}
public class Test{
public static void main(String[] args){
User.print();
}
}
作用在成员变量上:
同一个类的不同对象共享该成员变量。
public class User{
private static int value = 0;
...
public void setValue(int value1){
value = value1;
}
public int getValue(){
return value;
}
}
public class Test{
public static void main(String[] args){
User user1 = new User();
User user2 = new User();
user1.setValue(1);
System.out.println(user1.getValue()); //1
System.out.println(user2.getValue()); //1
}
}
作用在代码块上:
随着类的加载而加载,且只执行一次。
public class Parent {
static {
System.out.println("我是静态代码块");
}
public Parent(){
System.out.println("我是构造方法");
}
}
public class Test{
public static void main(String[] args){
Parent parent1 = new Parent();
Parent parent2 = new Parent();
}
}
打印信息:
我是静态代码块
我是构造方法
我是构造方法
作用在内部类上:
定义在类内部,当外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的,并且只要内部类不需要访问外围类对象,就应该使用静态内部类。
public class ArrayAlg {
public static class Pair{
private double first;
private double second;
public Pair(double first, double second) {
this.first = first;
this.second = second;
}
public double getFirst() {
return first;
}
public void setFirst(double first) {
this.first = first;
}
public double getSecond() {
return second;
}
public void setSecond(double second) {
this.second = second;
}
}
public static Pair minmax(double[] values){
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for(double v : values){
if(min > v) min = v;
if(max < v) max = v;
}
return new Pair(min,max);
}
public static void main(String[] args) {
ArrayAlg.Pair minmax = ArrayAlg.minmax(new double[]{1.1, 4.5, 6.7, 8.9});
System.out.println("min = " + minmax.getFirst() + " max = " + minmax.getSecond());
}
}
二、单例模式
1、介绍:
对于某个类使其只存在一个对象:
构造方法定义为私有的。
定义一个私有的成员变量,定义为static。
定义一个静态方法,获取这个变量。
优点:只生成一个对象,减少系统的性能开销。
2、应用场景:
-
网站的计数器,一般也是单例模式实现,否则难以同步。
-
应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,
因为只能有一个实例去操作,否则内容不好追加。 -
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库 资源。
-
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
-
Application 也是单例的典型应用 。
3、饿汉式:
class Single{
private static Single a = new Single();
private Single(){
}
public static Single instance(){
return a;
}
}
4、懒汉式:
class Single{
private static Single a ;
private Single(){
}
public static Single instance(){
if(a == null){
a = new Single();
}
return a;
}
}
问题:在多线程情况下,会出现线程安全问题。
public class Single{
private static Single a ;
private Single(){
}
public static Single instance(){
if(a == null){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
a = new Single();
}
return a;
}
public static void main(String[] args) {
for(int i = 0; i < 1000; i++){
new Thread(()->{
System.out.println(Single.instance().hashCode());
//默认的hashCode值为地址计算后的结果,地址相同值是一样的。
}).start();
}
}
}
结果:
2051899962
1802975985
2051899962
1802975985
1802975985
1802975985
1802975985
1802975985
1375911728
1375911728
1375911728
1375911728
1375911728
...
5、DCL单例:
双重检查机制
class Single{
private static Single a ;
private Single(){
}
public static Single instance(){
if(a == null){
synchronized (Single.class){
if(a == null){
a = new Single();
}
}
}
return a;
}
}
问题:由于new对象的操作不是原子性的,会存在指令重排序的情况。代码最终会被编译成多条汇编指令。
(1)给Single的实例分配内存
(2)调用Single()的构造函数,初始化成员字段
(3)将a对象指向分配的内存空间
6、 volatile的DCL
class Single{
private static volatile Single a ;
private Single(){
}
public static Single instance(){
if(a == null){
synchronized (Single.class){
if(a == null){
a = new Single();
}
}
}
return a;
}
}
三、代码块
1、静态代码块:
格式:
public class User{
....
static {
....
}
....
}
特点:
-
可以有输出语句。
-
可以对类的属性、类的声明进行初始化操作。
-
不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
-
若有多个静态的代码块,那么按照从上到下的顺序依次执行。
-
静态代码块的执行要先于非静态代码块。
-
静态代码块随着类的加载而加载,且只执行一次。
2、代码块:
格式:
public class User{
....
{
....
}
....
}
特点:
-
可以有输出语句。
-
可以对类的属性、类的声明进行初始化操作。
-
除了调用非静态的结构外,还可以调用静态的变量或方法。
-
若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
-
每次创建对象的时候,都会执行一次。且先于构造器执行。
3、执行顺序:
顺序:
类变量和静态代码块的级别相同,那个在前,那个先执行
实例变量和代码块的级别相同,那个在前那个先执行。
先初始化父类的静态代码 -> 子类的静态代码 (如果不创建实例,后面将无意义) ->
父类的非静态代码 -> 父类的构造方法 -> 子类的非静态代码 -> 子类的构造方法
示例:
public class Parent {
static {
System.out.println("我是父类的静态代码块");
}
{
System.out.println("我是父类的非静态代码块");
}
public Parent(){
System.out.println("我是父类的构造方法");
}
}
public class Son extends Parent{
static {
System.out.println("我是子类的静态代码块");
}
{
System.out.println("我是子类的非静态代码块");
}
public Son(){
System.out.println("我是子类的构造方法");
}
}
测试结果:
Son son = new Son();
我是父类的静态代码块
我是子类的静态代码块
我是父类的非静态代码块
我是父类的构造方法
我是子类的非静态代码块
我是子类的构造方法
参考资料:
尚硅谷资料,博客,马士兵老师公开课等。