JAVA static

一,static使用场景:

static修饰符被用来修饰一个类中的域变量、方法、初始化代码块以及嵌套的接口、嵌套的类;

或者被用来修饰一个接口中的域变量、嵌套的接口与嵌套的类。

二,static域变量的存储

static修饰的域变量存储在类的方法区内,试图使用以下方法耗费内存是徒劳的:

  class Go{
	static Go a =new Go();
	public void t(){}
	public static void main(String args[]){
	Go.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.t();
	}
}


因为a被存储在类Go的方法区,它指向了一个对象Go,当试图通过Go访问a时,每次拿到的都是方法区内的a。

如果是接口中的域变量将被存放在接口的方法区内。

三,static域变量初始化的时机

static修饰的域是在类的某个方法被调用时初始化的,比如类中的main方法被虚拟机调用,或者是类的构造方法被调用时,或是类的任何static方法被调用

因为这些方法被调用时,虚拟机就会去加载相应的类,将这些方法放入方法区,顺便也把static域一起初始化后一块儿放入方法区。

如果是接口中的被static修饰的域,因为接口中不存在静态方法,所以,这些域只能在被访问时被初始化。

四,static域变量+static初始化块+static方法

域变量按照代码的先后顺序被赋值,静态代码块中只能调用当前类的static方法。类加载完成时,方法区就有了static变量和static方法。

五,static与非static的桥梁

static方法要调用到非static方法必须通过构造方法这座桥梁,在static方法中唯一可以调用的非static方法就是构造方法(Thinking  in JAVA中说构造方法也是static的,只是省略了static关键字而已),不管是本类的构造方法还是其他类的构造方法。

1,通过构造方法,我们可以构造出实例来访问非static方法 2,通过构造方法,可以在内部继续调用非static方法  3,通过构造方法,利用他的副作用可以调用到非static初始化

代码块

另外,还需要注意一点,static与非static方法不能有相同的方法签名,但有一个例外。

public class Go {
	static Object Go(){return "";}
}

省略的默认构造方法与同名的static空参数方法不会有冲突。
只有显式声明的2个方法才会产生冲突。

java为什么要有这样的设定呢?static方法是使用类访问的,非static方法是使用类实例访问的,不会出现混乱的情况啊?

原因就在于java允许通过对象来访问static方法,这会造成调用的混乱。我始终觉得java的这个设计是个缺陷,gosling大叔应该在下个版本修改掉。(java总是想让新手用的更简单,但深入之后问题多多,垃圾回收也是例子)

你会发现在下面的例子中通过对象访问static方法真的让人烦躁,因为它会让人感觉方法是动态绑定的。

六,static+继承/实现

static的变量和static方法与类之间是静态绑定的,即在运行之前他们就被捆绑在了一起。

所以static变量和static方法在继承中不存在覆盖的情况,比如

public class Go extends Father{
	static int i=1;
	static void test(){
		System.out.println("son");
	}
	public static void main(String args[]){
		System.out.println(i);
		Father f = new Go();
		f.test();
		System.out.println(f.i);
		Go g = new Go();
		g.test();
		System.out.println(g.i);
	}
}
class Father{
	static int i=2;
	static void test(){
		System.out.println("father");
	}
}

1,JAVA不赞成使用对象去调用static方法,当然如果要强行调用,JAVA会寻找对象的声明类型,根据声明类型调用static方法或访问相应的变量。

如上例中的f.i被转换成了Father.i,g.i被转换成了Go.i.

2,而上述main方法的第一行说明了在static方法中直接访问static变量时JAVA的处理方式,首先该static方法找到自己所属的类,然后在该变量前

添加类名,如上例种的i被转换为Go.i。

下面的这个增强版的例子说明了这一点

public class Go extends Father{
	static int i=1;
	static void test(){
		System.out.println("son"+i);
	}
	public static void main(String args[]){
		System.out.println(Go.i);
		Father f = new Go();
		test();
		System.out.println(f.i);
		Go g = new Go();
		f.test();
		System.out.println(g.i);
	}
}
class Father{
	static int i=2;
	static void test(){
		System.out.println("father"+i);
	}
}

main方法调用Go类中的test方法,它首先找到与自己绑定的类Go,然后将方法中的i转换成了Go.i
3,上面说明了继承时父类与子类变量的分离性(即他们互不相干),但是,在继承或接口实现中又存在着父类的变量被子类复用的情况,我们可以去掉子类中

的i变量,不会报任何错误。

public class Go extends Father{
	static void test(){
		System.out.println("son"+i);
	}
	public static void main(String args[]){
		System.out.println(Go.i);
		Father f = new Go();
		test();
		System.out.println(f.i);
		Go g = new Go();
		f.test();
		System.out.println(g.i);
	}
}
class Father{
	static int i=2;
	static void test(){
		System.out.println("father"+i);
	}
}

这个时候Go的test方法找到了自己所绑定的类Go,将i装换成了Go.i,在Go的方法区内找不到变量i,由于继承的关系,它继续在Father类的方法区内寻找i。

将Father改成接口类型,也是一样

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值