static

static关键字可以应用于内部类(注意对于外部类是不能用static声明的)、成员方法、代码块以及成员变量(static不可以修饰局部变量)。

1.静态方法和实列方法的区别

1.1调用外部类

在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

package com.company;

import java.sql.SQLOutput;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.*;

public class Main{
    public static void main(String[] args) {
        Text.print();
        //或者实例化
        Text text = new Text();
        text.print();


    }
}
class Text{
    public static void print(){
        System.out.println("可以调用");
    }
}

1.2 访问本类成员

本例其实可以概括成一句话:静态方法只能访问静态成员,实例方法可以访问静态和实例成员。

之所以不允许静态方法访问实例成员变量,是因为实例成员变量是属于某个对象的,
而静态方法在执行时,并不一定存在对象。同样,因为实例方法可以访问实例成员变量,
如果允许静态方法调用实例方法,将间接地允许它使用实例成员变量,所以它也不能调用实例方法。
基于同样的道理,静态方法中也不能使用关键字this。

main()方法是一个典型的静态方法,它同样遵循一般静态方法的规则,所以它可以由系统在创建对象之前就调用。

package com.company;

import java.sql.SQLOutput;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.net.URL;
import java.util.concurrent.*;

public class Main{
    int a=1;
    static int b=1;
    public static void H(){
        System.out.println("H");
    }
    public void F(){
        System.out.println("F");
    }
    public static void go(){
        System.out.println("go");
        //允许访问静态方法H
        H();
        //允许访问静态属性
        System.out.println(b);

        //不允许访问实例方法
        //F();
        //不允许访问实例属性
        //System.out.println(a);
    }
    public void cc(){
        //全都可以访问
        go();
        H();
        F();
        System.out.println(a+" "+b);
    }
    public static void main(String[] args) {
        Text.print();
        Text text = new Text();
        text.print();
        go();

    }
}
class Text{
    public static void print(){
        System.out.println("可以调用");
    }
}

2.静态代码块

static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会自动执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次(除初始化静态变量的一些公共处理语句可以放在这里。比如:当一个类需要在被载入时就执行一段程序,这样可以使用静态程序块)。

public class Main {
    //2:赋初始值
    {
        System.out.println("匿名代码块");
    }
    //1.只执行一次
    static {
        System.out.println("静态代码块");
    }
    public Main(){
        System.out.println("构造函数");
    }
    public static void main(String[] args) {
	// write your code here
        Main m = new Main();
        /*
        输出结果
        静态代码块
        匿名代码块
        构造函数
         */
    }
}

3.静态导入包

package com.company;
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Main {
    public static void main(String[] args) {
        System.out.println(random());
        System.out.println(PI);
    }
}

4. static声明变量

被static修饰的变量,叫静态变量或类变量;没有被static修饰的变量,叫实例变量。
两者的区别是:

  • 静态变量属于类,在内存中只有一个复制(所有实例都指向同一个内存地址,节省空间),JVM在加载类的过程中完成静态变量的内存分配,可用类名.静态变量名直接访问(方便),当然也可以通过对象名.静态变量名来访问(但是这是不推荐的)。
  • 实例变量属于对象,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活),只能通过对象名.实例变量名来引用。

5. static声明方法

静态方法的好处就是不用生成类的实例就能直接调用,只要通过 类名.静态方法名 就可以访问,不需要耗费资源反复创建对象,因为在类加载之后就已经在内存中了。而非static方法是对象的方法,只有在对象被实例化以后才能使用。
  静态方法不能使用this和super关键字,不能调用非static方法(this涉及到当前对象,super 涉及到父类对象),只能访问所属类的静态成员变量和成员方法。因为当static方法被调用时,这个类的对象可能还没创建,即使已经被创建,也无法确定调用的是哪个对象的方法。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

例子:

  • 1.如果这个方法是作为一个工具来使用,可以声明为static,不用new一个对象出来就可以使用了,比如连接到数据库,声明一个 getConnection()的方法,就定义为静态的,因为连接到数据库不是某一个对象所特有的,它只作为一个连接到数据库的工具。
  • 2 实现单例模式:
    立即加载/“饿汉模式”
      什么是立即加载?立即加载就是在调用方法前,实例已经被创建了(其实是类加载的时候已经将对象创建完毕),常见的实现办法就是直接new一个private static的实例,然后通过public static的方法返回实例。而立即加载从中文的语境来看,有“着急”、“急迫”的含义,所以也称 为“饿汉模式”。
/**
 * 立即加载方式==饿汉模式
 * 此代码版本为立即加载,此版本代码的缺点是类加载时就要初始化对象
 */
public class Test {
    private Test() {}

    private static Test uniqueInstance = new Test();

    public static Test getInstance() {	
        return uniqueInstance;
    }
}

延迟加载/“懒汉模式”
  延迟加载就是在调用get()方法时实例才被创建。比如推迟一些高开销的对象初始化操作直到需要使用这些对象。常见的实现办法就是在get()方法中进行new实例化。而延迟加载从中文的语境来看,是“缓慢”、“不急迫” 的含义,所以也称为“懒汉模式”。但是在《Java并发编程实战》16.2节中给出说明,双检测机制已经被广泛地废弃 了——促使该模式出现的驱动力(无竞争同步的执行速度很慢,以及jvm启动时很慢)已经不复存在,因而它不是一种髙效的优化措施。延迟初始化占位类模式(即下面的内部类方式)能带来同样的优势,并且更容易理解。

/**
 * 延迟加载方式==懒汉模式
 * 使用双检测机制,尽量减小同步块的大小,同时保证线程安全
 */
public class Test {
	private Test() {}

	private volatile static Test uniqueInstance;

	public static Test getInstance() {
		if (uniqueInstance == null) {
			synchronized (Test.class) {
				if (uniqueInstance == null) {
					uniqueInstance = new Test();
				}
			}
		}
		return uniqueInstance;
	}
}

说明:

  • 1.如果不进行同步,可能多个线程同时检测到uniqueInstance == null,就会出现取出多个实例的情况。
  • 2.如果用synchronized同步整个getInstance()方法,会将线程中耗时较长,并且不需要同步的代码也锁上,导致效率太低。
  • 3.如果没有第二个if (uniqueInstance == null) 检测,同样可能出现:多个线程同时检测到uniqueInstance == null,从而取出多个实例的情况。
     注意:若在static变量前用private修饰,则表示这个变量只能在本类中的静态代码块或者静态成员方法使用,而不能通过类名直接引用

内部类方式–推荐:

public class Test {
    private static class MyObject {
        public static Test uniqueInstance = new Test();
    }

    private Test() {
    }

    public static Test getInstance() {
        return MyObject.uniqueInstance;
    }
}

在内部类方式中,使用了一个专门的类来初始化Test。JVM将推迟Test的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Test,因此不需要额外的同步。 当任何一个线程第一次调用getInstance时,都会使Test被加载和被初始化,此时静态初始化器将执行Test的初始化操作。

6. 静态对象的好处

静态变量、静态代码块和静态方法都属于静态对象,我们上面总结了他们的特点,这里说明静态对象的好处:

  • 1.静态对象的数据在全局是唯一的。任何一处地方修改了静态变量的值,在程序所有使用到的地方都将会体现到这些数据的修改。 非静态的东西你修改以后只是修改了他自己的数据,但是不会影响其他同类对象的数据。
  • 2.引用方便。直接用 类名.静态方法名 或者类名.静态变量名就可引用并且直接修改其属性值,不用get和set方法。
  • 3.节约空间。静态变量和静态方法属于类,在内存中只有一个复制(所有实例都指向同一个内存地址–方法区)
  • 4.由于静态对象的初始化是在类加载的初始化阶段进行,在实例化对象的构造函数返回时一定已经初始化完成,因此可以用于安全的发布对象,当然对象发布后的安全性要靠其他措施来保证。

7. 静态对象的坏处

类属性中被static所引用的变量,会被作为GC的root根节点。作为根节点就意味着,这一类变量是基本上不会被回收的。因此,static很容易引入内存泄漏的风险。

8.static声明内部类

静态内部类是指在一个类的内部,又定义了一个用static修饰的类。它可以不依赖于外部类实例对象而被实例化,但他不能访问外部类的普通成员变量和普通成员方法,只能访问外部类的static成员(包括私有类型)。
一个没有被static修饰的内部类,必须要这么声明。

OutterClass.InnerClass innerClass = new OutterClass().new InnerClass();

如果你使用了static修饰,那么你就可以这样使用内部类。

OutterClass.StaticInnerClass staticInnerClass = new OutterClass.StaticInnerClass();

这两种方式最大的区别就是,第一种方式,如果你想要获得InnerClass的实例,你必须有一个OutterClass的实例,所有其实这种方式你创建了两个实例,所以有两个new关键字。而第二种方式就好理解一些,静态内部类不依赖于外部类的实例存在,因此只需要直接创建内部类的实例就可以了,所以只有一个new关键字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值