static关键字
1.为什么要使用static关键字
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
2.类属性、类方法的设计思想
- 类属性(类变量)作为该类各个对象之间共享的变量。 在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
- 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
练习
编写一个类实现银行活期类账户,包含的属性有“帐号”、“密码”、“存款余额”、“年利率”、“最小余额”,定义封装这些属性的方法。编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性?
“年利率” 和 “最小余额”
3.static修饰的范围
- 类;
- 属性、方法、代码块、内部类;
(1)类变量(class Variable)
当某个属性的值是所有对象共享的,那么这样的属性应该声明为静态的,这样的属性我们称为类变量。
类变量具备以下特点:
- 随着类的初始化而初始化,优先于实例对象的创建
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接“类名.xx”调用
- 类变量的值存储在方法区
- 类变量的get/set方法也是静态的
(2)类方法(class Method)
当方法的功能实现、调用和该类的对象无关时,我们可以把这样的方法声明为static,我们称为静态方法。
- static修饰的方法,只要访问权限允许,可以通过"类名.方法"访问,因此static的方法也称为类方法。
- 在static方法内部只能访问类变量与类方法(static属性和方法),不能访问类的实例变量和成员方法(非static属性和方法)。
- static方法内部不能有this和super。
如果在静态方法中有局部变量与类变量重名,可以使用“本类名.xxx” 如果在静态方法中想要访问父类的静态成员,可以使用“父类名.xxx”
- static修饰的方法不能被重写,或者说不会被覆盖
因为静态方法是类方法,是属于类的,和对象无关
(3)代码块
初始代码块:一般来说是用于类或实例成员变量的初始化,分为静态代码块和非静态代码块(构造代码块)。
1、静态代码块
(1)可以为类变量(静态的属性)初始化
(2)随着类的初始化而初始化,只执行一次
(3)如果子类初始化时,它的父类没有初始化会先初始化父类
(4)在静态代码块中不能访问非静态成员(属性、方法、内部类)
(5)在静态代码块中不能使用this和super关键字
如果有重名问题,需要访问本类或父类的静态成员,那么可以使用“本类名.xx”、“父类名.xx”
如果没有重名问题,直接访问即可。
2、非静态代码块(构造块)
(1)可以为实例变量(非静态的属性)初始化
(2)随着对象的创建而初始化,每创建一个对象,就执行一次
(3)创建子类对象时,需要先为从父类继承的属性进行初始化,所以会导致父类的构造块和构造器先执行
(4)本类的非静态代码块优先于构造器执行
4、静态与非静态的访问原则(☆)
(1)同一个类中:
静态成员中不能访问非静态的成员
非静态成员中可以访问静态成员
(2)不同类中:
访问其他类的非静态成员必须使用“对象.非静态成员”的格式
访问其他类的静态成员建议使用“类名.静态成员”的格式,也可以使用“对象.静态成员”
5、初始化顺序
(1)类初始化
如果不需要创建对象,那么紧紧是类初始化;
(2)创建对象
如果要创建对象,那么要先看类是否之前初始化过,如果没有,那么要先初始化类,然后才能创建对象。
6、 clinit() 和 init()
A:
编译器会把“类变量的显式初始化”、“静态代码块”的内容按顺序合并为一个<clinit>的类初始化方法。
所以类的初始化过程就是执行<clinit>方法:
即类变量的显式初始化与静态代码块一定是一起执行的
而且因为类初始化只要一次,因此<clinit>只有一个,且只会执行一次。
B:执行子类的<clinit>方法时,如果父类也没有初始化,会先执行父类的<clinit>方法。
C:
编译器会把“非静态变量的显示初始化”、“非静态代码块”的内容按顺序合并到每一个构造器中,构成一
个个的<init>实例初始化方法,有几个构造器,就有几个<init>方法。
而且“非静态变量的显示初始化”、“非静态代码块”的内容在上面,原来构造器中的代码在下面。
因此对象的初始化,即执行它的<init>方法:
即非静态变量的显示初始化、非静态代码块和构造器中的代码是一起执行的
而且非静态变量的显示初始化、非静态代码块的内容先于构造器的代码执行。
D:执行子类的<init>方法时,一定会先调用父类的<init>方法,因为要先为从父类继承的属性先完成
初始化。(super([实参列表]))
E:记住不管是类变量还是实例变量都有默认值,再用<clinit>或<init>初始化之前,就有默认值。
(1)练习1
package com.atguigu.exer1;
public class TestStaticExer1 {
public static void main(String[] args) {
Son son = new Son();
}
}
class Father{
static{
System.out.println("(1)父类的静态代码块");
}
{
System.out.println("(2)父类的构造器");
}
Father(){
System.out.println("(3)父类的无参构造");
}
}
class Son extends Father{
static{
System.out.println("(4)子类的静态代码块");
}
{
System.out.println("(5)子类的构造器");
}
Son(){
System.out.println("(6)子类的无参构造");
}
}
(1)(4)(2)(3)(5)(6)
(2)练习2
package com.atguigu.exer2;
public class TestStaticExer2 {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Fu{
private static int i = getNum("(1)i");
private int j = getNum("(2)j");
static{
print("(3)父类静态代码块");
}
{
print("(4)父类构造代码块");
}
Fu(){
print("(5)父类构造器");
}
public static void print(String str){
System.out.println(str + "->" + i);
}
public static int getNum(String str){
print(str);
return ++i;
}
}
class Zi extends Fu{
private static int k = getNum("(6)k");
private int h = getNum("(7)h");
static{
print("(8)子类静态代码块");
}
{
print("(9)子类构造代码块");
}
Zi(){
print("(10)子类构造器");
}
public static void print(String str){
System.out.println(str + "->" + k);
}
public static int getNum(String str){
print(str);
return ++k;
}
}
(1)i->0
(3)父类静态代码块->1
(6)k->0
(8)子类静态代码块->1
(2)j->1
(4)父类构造代码块->2
(5)父类构造器->2
(7)h->1
(9)子类构造代码块->2
(10)子类构造器->2
(3)练习3
package com.atguigu.exer3;
public class TestStaticExer3 {
public static void main(String[] args) {
MyClass obj = new MyClass();
}
}
class MyClass{
static{
i = 100;//可以
// i++;//错误
MyClass.i++;
// System.out.println("(1)静态代码块 i=" + i);//错误
System.out.println("(1)静态代码块 i=" + MyClass.i);
}
{
j = 100;
// j++;
this.j++;
// System.out.println("(2)构造代码块j=" + j);
System.out.println("(2)构造代码块j=" + this.j);
}
MyClass(){
j = 200;
j++;
System.out.println("(3)构造器j=" + j);
}
private static int i = getNum("(4)i");
private int j = getNum("(5)j");
public static void print(String str){
System.out.println(str + "->" + i);
}
public static int getNum(String str){
print(str);
return ++i;
}
}
(1)静态代码块 i=101
(4)i->101
(2)构造代码块j=101
(5)j->102
(3)构造器j=201
查看:https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.2
(4)练习4
package com.atguigu.exer4;
public class T {
public static int k = 0;
public static T t1 = new T("t1");
public static T t2 = new T("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
}
static{
print("静态块");
}
public T(String str){
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
++i;
}
public static int print(String str){
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String[] args) {
}
}
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99