static 关键字的特点
用来修饰类的成员 - 修饰成员变量的称之为类变量 (静态变量), 修饰成员方法的称之为类方法 (静态方法).(属性拿 static 修饰完之后就不叫属性了, 他也就不属于任何对象了, 而是属于多个对象共享的, 就叫类变量或静态变量, 方法也一样)
当类被加载的时候就会被加载, 优先于对象的存在.
用来修饰与语句块 - 称之为静态代码块. 先于构造方法之前执行, 只会执行一次. 用来对静态成员做初始化
静态修饰的成员被所有的对象共享
调用的时候可以直接通过类名. 成员来进行访问.
static 关键字注意事项
1. 静态方法中只能访问外部的静态成员 (为什么? 当我们去调用静态方法的时候, 对象都没产生, 对象没产生怎么可能有属性呢, 因为属性是属于某个对象的对吧) 2. 静态方法中不能出现 this 关键字 (为什么? 同样的道理, 当我们去调用静态方法的时候, 对象都没产生, 对象没产生 this 又是指谁呢?)
ok 上代码!
- public class StaticDemo {
- public static void main(String[] args)
- {
- Device device1 = new Device();
- device1.showName();
- device1.showEnergy();
- Device device2 = new Device();
- device2.energy++;
- device2.showName();
- device2.showEnergy();
- }
- }
- class Device
- {
- private String name="device1";// 普通的属性
- public int energy=2;// 普通的属性
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getEnergy() {
- return energy;
- }
- public void setEnergy(int energy) {
- this.energy = energy;
- }
- public void showName()
- {
- System.out.println(name);
- }
- public void showEnergy()
- {
- System.out.println(energy);
- }
- }
看下输出结果:
- device1
- 2
- device1
- 3
ok, 现在分析下, 我第一个对象的 energy 属性初始值时 2, 第二个对象的 energy 属性我用 public 修饰了, 所以我直接可以调用下, 然后加一了, 打印出来的结果两个对象是不一样的. 这就说明了什么? 每个对象都有他自己的独立的一份属性. 对不对?~ 对的哈.. 这句话很重要~~
那我现在如果把 name 这个属性改成用 static 来修饰的话, 现在会怎么样? public static String name="device1";// 静态变量, 他现在不属于任何一个对象了, 被多个对象共享, 他现在是属于类的, 他也称为类变量
现在我们把调用方法改成这样
- public static void main(String[] args)
- {
- Device device1 = new Device();
- device1.name = "cyy";
- device1.showName();
- device1.showEnergy();
- Device device2 = new Device();
- device2.energy++;
- device2.showName();
- device2.showEnergy();
- }
现在 name 不是个属性了, 他现在是个静态变量, 我在 device1 里面把他设置成 cyy, 现在打印结果来看下.
cyy
2
cyy
3
看到没两个 name 都变成 cyy 了. 这说明了什么? 这是不是说明了, 这个 name 大家都共享了呀, 不再是每个对象独立拥有的了, 对吧! 那既然他都不是共享的了, 那我们访问的时候是不是就可以直接 Device.name 就可以了啊, 因为他现在不属于对象了, 他现在属于类的. 而 age 可不可以直接通过 Device.age 来访问啊, 当然不可以, 因为他现在并不属于类对吧.
好的, 现在如果我们把一个方法改成静态的呢, 比如这样:
- public static void showName()
- {
- System.out.println(energy);
- System.out.println(name);
- }
如果这么写的话, 是不是会报错呀, 编译时就会报错, 会提示: Non-static field 'energy' cannot be referenced from a static context
什么意思? 大概意思就是说, 你静态方法不能访问非静态变量~ 为什么?! 因为你静态方法调用的时候, 还没有创建对象呢, 人家 energy 是个属性, 又不是静态变量, 你会跑的时候, 人家还没出生呢, 你咋能去找人家玩儿呢? 对吧, 所以静态方法不能访问非静态变量, 静态方法中不能使用 this, 只能访问外部静态的东西.
那非静态方法可不可以访问静态变量? 思考一下? 答案当然是可以的, 想像一下, 静态变量在你对象创建之前就已经分配好内存空间了, 已经存在了, 你对象创建完之后, 他已经存在了, 所以肯定是可以访问的, 对吧. 总结就是静态的只能访问静态的, 非静态的都可以访问.
还有个 static 语句块
他什么时候执行呢?
在类被加载的时候就会执行, 只会执行一次, 用来对静态变量进行初始化. 优先于构造方法的执行.
上代码:
- class Device
- {
- public static String name;// 普通的属性
- public int energy=2;// 普通的属性
- //static 语句块
- static {
- name = "device1";
- System.out.println("=== 我是静态语句块里的"+name);
- }
- // 构造方法
- public Device(String name)
- {
- this.name = name;
- System.out.println("=== 我是构造方法里的"+name);
- }
- public static void showName()
- {
- System.out.println(name);
- }
- public void showEnergy()
- {
- System.out.println(energy);
- }
- }
我们现在使用下:
- Device device1 = new Device("cyy");
- Device device2 = new Device("cyy513");
看下打印结果:
=== 我是静态语句块里的 device1
=== 我是构造方法里的 cyy
=== 我是构造方法里的 cyy513
发现了吧, 我 new 了两个对象, 但是静态语句块里的 system.out.println 只打印了一次, 说明啥 static 语句块只执行一次, 不管你创建多少次对象. 而且我是先于构造方法执行的.
那会有人说, 那我以后都不用属性了, 我全部用 static 变量, 多好, 多方便, 其实这样有很多缺点:
破坏了 java 面向对象的封装性
static 变量的生命周期比较长, 程序结束的时候, 他的内存才会释放, 而属性呢, 这个对象运行完了他的属性内存是不是就释放了呀.
那我们什么时候用 static 呀?
当我们一个类里面, 没有任何属性, 只有方法 , 而这个方法是为其他的类服务的, 这个时候适合用 static 的.
接下来我们了解下单例模式
定义:
顾名思义, 单例模式的意思就是只有一个实例, 单例模式确保某一个类只有一个实例, 而且自行实例化并为整个系统提供这个实例, 这个类称为单例类.
通俗的说, 就是我有一个类, 整个系统就一个实例, 而且他是自己创建自己, 他必须对外提供个方法, 把我自己给你.
上代码:
- class SingleTon
- {
- private SingleTon singleTon = new SingleTon();
- /**
- * 构造方法一定不能是公开的, 不然别人就可以随便构造了.
- * 所以构造方法必须是 private, 对吧
- */
- private SingleTon()
- {
- }
- // 如果我这么写, 会出现什么问题
- public static SingleTon getInstance()
- {
- return singleTon;
- }
- }
是不是会出现之前说的那种错误呀, 静态方法不能引用非静态变量. 对不对. 所以要怎么做? 是不是给前面 singleton 加上个 static 就可以了是吧. 因为这个 SingleTon 是静态的在内存里只有一份对吧~ 正确写法,
- class SingleTon
- {
- public static SingleTon singleTon = new SingleTon();
- private SingleTon()
- {}
- public static SingleTon getInstance()
- {
- return singleTon;
- }
- }
这种方式称之为饿汉式. 为什么? 因为我在调用 getInstance() 之前这个对象是不是就已经产生了呀, 因为它是静态的嘛, 在类加载时候, 就已经 new SingleTon() 了呀. 那我们现在有没有更好的方法呢? 比如在 getInstance() 的时候再去创建这个对象呀? 当然可以呀~ 这么写就可以了.
- class SingleTon
- {
- public static SingleTon singleTon = null;
- /**
- * 构造方法一定不能是公开的, 不然别人就可以随便构造了.
- * 所以构造方法必须是 private, 对吧
- */
- private SingleTon()
- {
- }
- // 如果我这么写, 会出现什么问题
- public static SingleTon getInstance()
- {
- if (singleTon==null)
- {
- singleTon = new SingleTon();
- }
- return singleTon;
- }
- }
这种就是懒汉式加载.
不管是懒汉式还是饿汉式, 是不是目的就是一个, 让他整个系统中只有一个实例.
来源: https://juejin.im/post/5be39386518825594036684e