static 静态
静态:static
用法:是一个修饰符,用来修饰成员(成员变量,成员方法,代码块)
当成员被静态修饰后,就多了一个调用方式,除了对象调用后,还可以直接类名调用,例如 类名.
用static修饰的程序代码表示静态代码块,当Java虚似机加载类时,就会执行该代码块;
static特点:
1.随着类的的加载而加载
也就是说静态会随着类的消失而消失,说明他的生命周期最长。
被static所修饰的成员变量和成员方法表明归某个类所有,它不依赖 于类的特定实例,被类的所有实例共享。
2.优先于对象存在(方法区静态区)
明确一点,静态是先存在,对象是后存在的。
3.被所有对象所共享
4.可以直接被类名所调用
※ static 变量:
成员变量:定义在类里面、方法外面的变量, 分两种:
a.实例变量;
b.静态变量;形式和实例变量类似,在实例变量前面加static关键字;
静态的常量赋值
a.变量后面可以赋值
b.静态代码块可以
c.匿名代码块和构造器不能赋值
static变量和实例变量的区别:
.static变量对于每个类而言在内存中只有一个,能被类的所有实例所共享;实例变量对于每个类的每个实例都有一份,它们之间互不影响;
.Java虚拟机在加载类的过程中为static变量分配内存(存于方法区),实例变量在加载完类后创建对象时分配内存(存于堆内容中);
.static变量可以直接通过类名访问,实例变量通过引用类型变量访问;
举例: public class Counter {
public int count1 = 0;
//static修饰的静态全局变量
public static int count2 = 0;
public static void main(String[] args) {
Counter counterA = new Counter();
Counter counterB = new Counter();
//非静态变量必须先构建对象,通过对象名.属性名调用
counterA.count1++;
counterA.count2++;
counterB.count1++;
counterB.count2++;
//static修饰的静态全局变量既可以通过构建对象调用,
//也可以直接通过类名.属性名调用
system.out.println(Counter.count2);
}
}
※ static 方法
成员方法分为静态方法和实例方法。用static修饰的方法叫静态方法,或类方法。静态方法也和静态变量一样,不需要创建类的实例,可以直接通过类名来访问。
举例:public class Sample {
//static修饰的静态方法
public static int add(int x, int y) {
return x+y;
}
public void method() {
int result = Sample1.add(1,2);
System.out.println("result= " + result);
}
public static void main(String[] args){
Sample s=new Sample();
s.method();
//static修饰的静态方法既可以通过构建对象调用,
//也可以直接通过类名.方法名调用
s.add(x,y);
Sample.add(x,y);
}
}
a. static方法可以直接访问所属类的实例变量和实例方法,直接访问所属类的静态变量和静态方法;
静态属于类,静态不能访问非静态的(非静态属于对象;
静态的东西类加载过程分配内存,在方法区,堆中构建对象调用静态时只是建立借口,当对象更改其内容时,其他对象中该静态也会改变。(静态变量被所有对象共享(值时同一个));
静态的方法可以访问静态的属性和方法(也可能是其他类的)。
注:
1) 不能使用this关键字;
2) 不能使用super关键字,super关键字用来访问当前实例从父类中继承的方法和属性。super关键字与类的特定实例相关;
3) 静态方法必须被实现。静态方法用来表示某个类所特有的功能,这种功能的实现不依赖于类的具体实例,也不依赖于它的子类。既然如此,当前类必须为静态方法提供实现。
b. 父类的静态方法不能被子类覆为非静态方法。以下代码编译出错。
public class Base {
public static void method() {}
}
public class Sub extends Base {
//编译出错,静态方法不能被重写为非静态方法
public void method() {}
}
c. 父类的非静态方法不能被子类覆盖为静态方法;
public class A{
public void say(){}
}
public class B extends A{
//编译出错,非静态方法不能重写为静态方法
public static void say(){}
}
※ static 代码块(没有名字的方法)
格式
static{}
匿名代码块
特点
. 在构建对象的时候调用一次
. 在对属性显示初始化之后,构造器调用之前执行的
. 不能传参数,一般给全局变量赋值
. 静态非静态方法都能调用
静态匿名代码块
特点
.随着类的加载而执行,只执行一次,并且优先于主函数。
.用于初始化的,顺序在构造函数之前。
类中可以包含静态代码块,它不存于任何方法中。在Java虚拟机中加载类时会执行这些静态代码块。如果类中包含多个静态代码块,那么Java虚拟机将按照它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。(思考:什么时候JVM对一个类进行类加载)执行位置,类加载.class文件,给静态变量赋默认值,查看静态变量有没有指定具体的值,指定了,把默认值替换,接下来执行静态代码块,静态代码块不能调用非静态的东西
作用:给静态变量赋值
public class Sample {
static int i = 5;
static {//第一个静态代码块
System.out.println("First Static code i="+i++);
}
static {//第二个静态代码块
System.out.println("Second Static code i="+i++);
}
public static void main(String[] args) {
Sample s1 = new Sample();
Sample s2 = new Sample();
System.out.println("At last, i= "+i);
}
}
class staticCode
{
int num=9;
staticCode()
//匿名代码块
{
System.out.println("a");
}
//静态匿名代码块
static{
System.out.println("b");//此处若为
//System.out.println("b"+num);编译出错
} //因为静态先执行,对象还未建立,只能访问静态成员
{
System.out.println("c"+num);//此处若为
//System.err.println("c"+num);编译不出错
} //执行顺序也是在构造函数之前
public static void show()
{
System.out.println("haha");
}
staticCode(int x){
System.out.println('d');
}
}
class StaticDemo {
public static void main(String[] args) {
new staticCode(4);
}
}
类的构造方法用于初始化类的实例,而类的静态代码块则可用于初始化类,给类的静态变量赋初始值。
静态代码块与静态方法一样,也不能直接访问类的实例变量和实例方法,而必须通过实例的引用来访问它们(非静态都需构建对象)。
//Student s = new Student();
new一个对象的时候JVM都做了那些事情:
. 之前没有进行类加载
1.类加载(之前没有进行类加载),同时初始化类中静态的属性(赋默认值),查看静态变量是否有制定的值,有替换默认值
2.执行静态代码块,在执行子类静态代码块
3.分配内存空间,同时初始化非静态的属性(赋默认值)
4.调用父类构造器
5.父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
6.执行匿名代码块
7.执行构造器
8.返回内存地址
例子:
package com.briup.ch06;
public class Test {
public static void main(String[] args) {
A a = new B();
}
}
class A{
protected String name = "lisi";
public A() {
System.out.println("父类构造器A");
System.out.println("父类构造器A中调用test方法开始,由于子类重写过test方法所以这里执行子类的test方法");
test();
System.out.println("父类构造器A中调用test方法结束");
}
public void test(){}
}
class B extends A{
private String name = "tom";
{
System.out.println("子类匿名代码块中:"+name);
}
public B() {
System.out.println("子类构造器B");
}
public void test(){
System.out.println("test方法中:this.name="+this.name);
System.out.println("test方法中:super.name="+super.name);
}
}
打印结果:
父类构造器A
//子类构造器调用父类构造器
父类构造器A中调用test方法开始,由于子类重写过test方法所以这里执行子类的test方法
test方法中:this.name=null
//这个时候父类构造器还没有执行完 所以子类中的属性不会显示赋值 所以只有初始的默认值null
test方法中:super.name=lisi
//这个时候父类构造器开始调用 了 所以父类中的属性已经有了显示赋的值了
父类构造器A中调用test方法结束
子类匿名代码块中:tom
//这个时候父类构造器已经调用结束 所以子类中的属性已经有了显示赋的值了
子类构造器B类中的属性的显示赋值的时机 是在 父类构造器执行完之后和子类的匿名代码块执行之前的某个
. 之前已经进行了类加载
1.分配内存空间,同时初始化非静态的属性(赋默认值)
2.调用父类构造器
3.父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
4.执行匿名代码块
5.执行构造器
6.返回内存地址
※ 静态导入
静态导入也是JDK5.0引入的新特性。
要使用静态成员(方法和变量)我们必须给出提供这个静态成员的类。使用静态导入可以使被导入类的静态变量和静态方法在当前类中可以直接使用,使用这些静态成员无需再在前面写上他们所属的类名。
静态导入之后,可以直接在类中使用变量名
//例如:
import static java.lang.Math.random;
import static java.lang.Math.PI;;
public class Test {
public static void main(String[] args) {
//之前是需要Math.random()调用的
System.out.println(random());
System.out.println(PI);
}
}
静态使用注意事项:
1.静态方法只能访问静态成员
非静态方法既可以访问静态成员也可以访问非静态
2.静态方法中不可以定义 this super关键字
3.主函数是静态的
静态利弊:
利处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象都存储一份
弊处:生命周期过长
访问出现局限性(静态虽好,只能访问静态)
1.
class Person
{
String name;
static String country="Ch";
public void show(){
System.out.println(name+","+country);
}
}
public class Demo1 {
public static void main(String[] args) {
Person p=new Person();
p.name="An Qi";
p.show();
}
}
2.
class Person
{
String name;
static String country="Ch";
public void show(){
System.out.println(name+","+country);
}
}
public class Demo1 {
public static void main(String[] args) {
System.out.println(Person.country);
}
}
3.
class Person
{
String name;
static String country="Ch";
public static void show(){
System.out.println(country);
//静态方法只能访问静态变量。
}
}
public class Demo1 {
public static void main(String[] args) {
System.out.println(Person.country);
}
}
什么时候使用静态
要从两方面下手
因为静态修饰的内容有成员变量和方法
当对象中出现共享数据时,该数据被静态所修饰
当对象中的特有数据要定义成非静态存在于堆内存中
什么时候定义静态方法?
当功能内部没有访问到非静态数据(对象的特有数据),该功能定义为静态
class Person{
String name;
public static void show(){
System.out.println("haha");
}
public void show2(){
System.out.println(name+"haha");
}
}
public class Demo1 {
public static void main(String[] args) {
Person.show();
Person p=new Person();
p.show2();
}
}