静态域[详解]

不知道静态域是什么,目前有两种想法

1是代表static修饰的属性、方法等的集合,即所有static修饰的都算

2是认为仅仅代表静态代码块,即

static {
    
    }

下面正式研究"何为静态域"

        查到的文章基本分静态域、静态常量、静态方法这几块进行介绍,内容都大差不大但都没有很明确指出何为静态域静态域和其他静态相关(静态属性、静态方法等)之间的关系

下面也分静态域、静态常量、静态方法这几块进行讲解,最后总结再解答我前面的问题

静态域说法1

如果将类中的域定义为static则这个域属于这个类,而不属于这个类的某个对象。每个类中只有一个这样的域,而每一个类对象对于所有的实例域(即没有定义为static的域)都有自己的一份拷贝

例如
class Employee{
    ……
    private int id;
    private static int nextId = 1;
}

如果有1000个Employee类的实例则有1000个实例域id,但是只有一个静态域nextId。即使没有一个Employee实例,静态域nextId也会存在。静态域也称为类域,它属于类,不属于任何对象

小结

就是static修饰的域可以叫静态域、类域;静态域是类级别,不是对象级别的、对象不存在,静态域也会存在

静态域说法2

        静态域又称为类域,顾名思义在静态域中的变量或者常量以及方法在整个类中都是有效的。一般我们用static关键字来定义静态域,被static修饰的变量或者常量以及方法都处在静态域中->这里比说法1明确多了,说法1对静态域的定义就是static修饰的域。而域具体代表哪块屈区域并没有说明。再看这里,第二句话指明静态域就是包含所有被static修饰的东西。也就是所有被static修饰的回收集到一块专门的空间中,类似类的生命周期中的初始化阶段执行的clinit()。对不对,继续看

静态域说法3

一个静态域、多个实例域,即前面说不管实例存不存在静态域都会存在且共享这一个,而实例域则和实例相关联。可通过下面例子验证

public class EmployeeTest {
    public static void main(String[] args){
        Employee[] temp = new Employee[5];

        for (int i = 0; i < 5; i++)
            temp[i] = new Employee();

        for (int i = 0; i < 5; i++){
            temp[i].setID();
            System.out.println("My number is: " + temp[i].getID());     }
    }
}

class Employee{

    private int id;
    private static int nextId = 1;

    public Employee(){}

    public void setID(){
        id = nextId;
        nextId++;     }

    public int getID(){
        return id;      }

}

静态域说法4

如果别人问你static的作用

你说静态修饰 类的属性 和 类的方法,别人认为你是合格的

如果你说 可以构成 静态代码块,那别人认为你还可以

如果你说可以构成 静态内部类,那别人认为你不错

如果你说了静态导包,那别人认为你很OK->没听过,不过下面挑重点讲会讲,不过也是了解即可的程度

静态内部类

1、内部类一般只为其外部类使用

2、内部类提供了某种进入外部类的窗户,内部类存在外部类的引用,所以内部类可以直接访问外部类的属性

3、最吸引人的原因:每个内部类都能独立地继承一个接口,而无论外部类是否已经继承了某个接口。因此内部类使多重继承的解决方案变得更加完整

        外部类按常规的类访问方式(以对象的方式)使用内部 类,唯一的差别是外部类可以访问内部类的所有方法与属性,包括私有方法与属性。外部类访问内部类需要创建对象访问。有一点需要注意,内部类不能访问外部类所在的局部变量,只能访问final修饰的局部变量

内部类向上转型

内部类也可以和普通类一样拥有向上转型的特性。将内部类向上转型为基类型,尤其是接口时,内部类就有了用武之地。如果内部类是private的,只可以被它的外部类问从而完全隐藏实现的细节

方法内的类

方法内创建的类(注意方法中也能定义类)不能加访问修饰符。另外方法内部的类也不是在调用方法时才会创建的,它们一样也被事先编译了

静态导包

        静态导包就是java包的静态导入,用import static代替import静态导入包是JDK1.5中的新特性

一般我们导入一个类都用 
import com…..ClassName;

而静态导入是这样
import static com…..ClassName.*;

可见多个static,还有就是类名ClassName后面多了个.*

意思是导入这个类里的静态方法。当然也可以只导入某个静态方法,只要把 .* 换成静态方法名即可。然后在这个类中就可以直接用方法名调用静态方法而不必用类名.方法名 的方式来调用->就是调用静态属性、静态方法啥的直接写属性名、方法名即可

好处

这种方法的好处就是可以简化一些操作

例如打印操作
System.out.println(…);    就可以将其写入一个静态方法print(…)
在使用时直接print(…)就可以了
print(…)

但是这种方法建议是在有很多重复调用的时候使用;如果仅有一到两次调用不如直接写来的方便

example

在Java 5中,import语句得到了增强,可以提供甚至更加强大的减少击键次数功能。虽然一些人争议说这是以可读性为代价的。这种新的特性成为静态导入,当你想使用static成员时可以使用静态导入(在API中的类和你自己的类上都可以使用该特性)

下面是静态导入前后的代码实例

在静态导入之前

public class TestStatic {

    public static void main(String[] args) {

        System.out.println(Integer.MAX_VALUE);

        System.out.println(Integer.toHexString(42));    }

}

在静态导入之后

import static java.lang.System.out;
import static java.lang.Integer.*;

public class TestStaticImport {

    public static void main(String[] args) {

        out.println(MAX_VALUE);

        out.println(toHexString(42));    }}

确实,代码节省了,可读性下降。至此静态导包介绍完了,下面讲一些注意点 

1、虽然该特性通常称为“静态导入”,但语法必须是import static不能说static import,后面再跟你想导入的static成员的完全限定名称或者通配符。在本例中,我们是在System类的out对象上进行静态导入

2、提防含糊不清的命名static成员

        如果你对Integer类和Long类执行了静态导入,引用MAX_VALUE将导致一个编译器错误。因为Integer和Long都有一个MAX_VALUE常量,所以Java就不知道你在引用哪个MAX_VALUE

所以记住,1、你可以在static对象引用、常量(记住,它们是static 或final)和static方法上进行静态导入;2、这种方式建议是在有很多重复调用的时候使用

静态域说法5

,英文文档中为field,也就是我们常说的字段、属性。

比如类的字段(属性),局部的、全局的。所以、所谓的域其实是“field”的翻译,那么静态域就只能是静态属性了

java中的域分为两类:静态域(一个类中只有一个这样的域)、实例域(每一个对象对于所有的实例域都有自己的一份拷贝)

下面举例认识一下域

public class InitialOrderTest {               
     静态变量          
    public static String staticField = "静态变量";          
     变量          
    public String field = "变量";                  
     静态初始化块        
    static {          
        System.out.println(staticField);  
        System.out.println("静态初始化块");  
    }                  
     初始化块              
    {          
        System.out.println(field);  
        System.out.println("初始化块");        
    }                  
     构造器          
    public InitialOrderTest() {  
        System.out.println("构造器");       }
                  
    public static void main(String[] args) {  
        new InitialOrderTest();              
    }          
}

前面说静态域只能单单指静态属性了,可下面还是将静态变量、静态初始化块归为静态域

将变量、初始化块归为实例域

构造器另算

所以静态域现在指的是静态相关的,即static修饰的都算在内

域的初始化

属于拓展内容

初始化域即给域赋值有以下几种方式

      1)赋予默认赋值
      2)声明变量时同时赋值
      3)块赋值(实例块和静态块)
      4)构造器赋值

如果同时存在以上几种赋值方式,那么域的最终值会是哪个呢?这里涉及到域的初始化顺序的问题

域的初始化分为两种情况     

一种是在建立对象即进行类的实例化时域的初始化
另一种是在不建立对象,只装载类的时候域的初始化

一、构建对象时域的初始化

构建对象,就是用new class()语句建立一个新的类的对象。在这种情况下,类中的域是按照如下顺序进行初始化的

赋予默认值-->(静态域、静态块)-->(实例域、实例块)-->构造器

举例:假设一个域即变量int a,当建立对象时,首先赋予它一个默认值,int类型的默认值为0;如果a为静态域并且在静态块中被赋值,那么就按照静态域和静态块在程序中出现的顺序先后执行;如果同时还在实例块中被赋值,则再执行实例块中的赋值语句(静态域不可能再是实例域);最后执行构造器中的赋值语句(如果在构造器中有被赋值的话)。如果变量a是实例域,则不会有静态域和在静态块中赋值(不能在静态块中给实例域赋值)的情况,其他同前所述

二、装载类时域的初始化化

有两种情况是只装载类而不实例化类
一是用java classname执行程序时
二是用classname.statement调用类的静态域或静态方法时

装载类时这个类并没有被实例化也就不能形成对象,所以不能对实例域进行初始化。因此只有静态域、静态块才能被初始化执行,执行规则同构建对象时的规则

静态域说法6

其实前面的说法看下来就明白何为静态域了,静态相关的就是静态域,即static修饰都算

下面借助《java核心技术卷一》再来看看静态域

上面介绍了何为"声明",即数据类型 变量 分号的形式是"声明" 。讲这个是为了和"初始化"区分开来

"初始化"就是"声明"的基础上进行赋值 

因为经常"声明"、"初始化"用词不规范,所以上面特意讲讲。再"声明"有时也"定义"代替,不过后者不怎么用

所以,大多数文章就是直接cv书上的内容罢了。静态域到此已经懂了,下面静态常量、静态方法啥的算拓展吧 

静态常量

        如果一个域被定义为static final,则这个域就是一个静态常量。静态变量使用的比较少,但是静态常量却使用的比较多。在实际的开发中,静态常量可以做到改一处能多处使用,能大大的减少修改和出错的地方

不能省略任何一个关键字。若是少了static,则该域变成了一个实例域,需要由类对象对其进行访问。若是省略了final,则该域变成了静态域,静态方法可以对其进行修改

建议:如果你要使用static修饰常量并且权限修饰符为public的话,建议加上final。不然的话,你的常量任意一个类都可以进行修改,那么这个常量也就失去了意义

静态变量和非静态变量的区别是

静态变量被所有的对象所共享,在内存中只有一个副本【存放在方法区】。它当且仅当在类初次加载时会被初始化【加final和不加final的static变量初始化的位置不一样】。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
 

static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。是因为它的特性:只会在类加载的时候执行一次。所以很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行

静态方法

        静态方法是一种不能向对象实施操作的方法。Math的pow()就是一个静态方法,在运算时不使用任何Math对象。换句话说,没有隐式的参数this。因为静态方法不能操作对象,所以不能在静态方法中访问实例域,但是静态方法可以访问自身类中的静态域。可以使用对象调用静态方法,但是这样容易引起混淆。因为计算的结果与对象毫无关系,建议还是使用类名而不是类对象调用静态方法->没有隐式参数,隐式参数指的是this。换句话说,没有隐式的参数。可以认为静态方法是没有this参数的方法(在一个非静态的方法中,this参数表示这个方法的隐式参数)

例如
public static int getNextId( ){
    return nextId;    }

如果去掉其中的关键字static,它就成了非静态方法,但是也可以访问类中的静态域,这时就需要由该类的对象来调用该函数

在下面两种情况下使用静态方法

1.一个方法不需要访问对象的状态(对象的实例域),其所需的参数都是通过显式的提供

2.一个方法只需访问类的静态域

类方法可以调用其他类的static方法。可以在类方法中生成实例对象再调用实例方法

调用类方法的方式:可由实例对象调用,还可以由类名直接调用

类中的实例方法可以用类名直接调用吗?

不可以。实例方法需要先创建实例,然后才可以调用实例的方法

工厂方法

我不熟,所以直接cv了,不对下面进行研究

静态方法还有一种常见的用途,例如:NumberFormat类使用工厂方法产生不同风格的格式对象

NumberFormat currencyFormatter =NumberFormat.getCurrencyInstance();

NumberFormat percentFormatter=NumberFormat.getPercentInstance();

doublen n= 0.3;

System.out.println(currencyFormatter.format(n));//输出 ¥0.30

System.out.println(percentFormatter.format(n)); //输出 30%

为什么NumberFormat类不利用构造器完成这些操作呢?主要的原因有两个:

1、无法命名构造器。构造器的名字必须与类名相同,但是,这里希望将得到的货币实例和百分比实例采用不同的名字。

2、当使用构造器时,无法改变所构造的对象类型。而Factory方法将返回一个DecimalFormat类对象,这是NumberFormat的子类。

5、main方法

我们学习java的时候,程序中大多会有一个main 方法,我们都称作程序的入口,main方法不对任何对象进行操作,事实上,在启动程序的时候,还没有任何一个对象,静态的main方法将执行并创建程序所需的对象


常见的笔试面试题(再看看)

class Person{

    static{

        System.out.println("person static");    }

    public Person(String str) {

        System.out.println("person "+str);    }

    }

public class Test {

    Person person = new Person("Test");

    static{

        System.out.println("test static");    }

    public Test() {

        System.out.println("test constructor");    }

    public static void main(String[] args) {

        new MyClass();    }
    }



class MyClass extends Test {

    Person person = new Person("MyClass");

    static{

        System.out.println("myclass static");

        }

    public MyClass() {

        System.out.println("myclass constructor");     }    }
我以为结果是

test static 

myclass static

person static 

myclass con

实际是

test static

myclass static

person static

person Test

test constructor

person MyClass

myclass constructor

解释:首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值