Java static基本认知

Posted 天堂里外

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java static基本认知相关的知识,希望对你有一定的参考价值。

 

 

一、 static的用途

  在Java编程思想中有这么一句话:“static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途”

  这句话在我理解来说就相当于是static方法是属于类的,而非静态方法是属于对象的。在实际应用中就相当于非静态方法只有在new出相应的对象来才能调用,而静态方法只需要’类名.方法’即可调用。

  在什么情况下我们需要使用static关键字呢?

a、 对于一个类相对固定的一些参数及方法

b、 一些需要预先定义的参数及方法

c、 一些调用广泛的公共参数及方法

在我看来static关键字的核心是避免一些内存的重复占用,我们想象一个java工程在开始运行时,会通过jvm分配一片空间,在这一片空间中会预先加载所有需要的类,然后就是我们在new对象时不停的占据内存。但是例如枚举类(之后称为Contact)中的参数是会在其他很多对象和函数中调用,如果我们每次调用的时候都需要重新new一个Contact对象,岂不是会在内存中重复占用非常多的资源。这时我们就需要static关键字修饰Contact类中的参数,这样在我们预先加载所有的类时,Contact类中通过static修饰的参数就会随着Contact类一起被加载在内存中,在我们需要的时候就可以直接通过Contact.参数名来调用这个参数而不需要重复占用另外的内存。(如果在面试中刚好被问到了,答出来应该会很加分的)

  1、 修饰类变量

    static变量一般也被称为静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

  static成员变量的初始化顺序按照定义的顺序进行初始化。

接下来举例说明:

 

这是一个基本的Man类,并生成了两个对象,但是从类的定义上来看,我们可以认为sex参数的值固定为男,但生成了两个对象就相当于在内存在将sex参数存储了两遍,这无疑是一种内存上的浪费。而这时我们给sex参数加上static关键字会变成什么样呢?

 

我们只需要给Man类中的sex参数赋值就会使所有的Man对象sex值进行改变。

虽然sex参数成为了Man类的共享属性,但是实际中我们很少这么使用,因为这样会使得该参数很难控制,因为它在任何地方都可能被改变。所以我们一般采用其他的使用方法:

 

 

通过private+static修饰count属性,并在Man的构造函数中对Man对象的数量进行统计并赋值给id属性来区分不同的Man对象。并且因为private修饰外界无法随意更改countid参数。

  2、 修饰类方法

与静态参数使用场景基本相同,但是需要注意的是,同一个类,静态方法中无法直接调用非静态参数及非静态方法,但非静态方法中可以直接调用静态方法及静态参数。

 

 

  3、 静态代码块

静态代码块主要作用是在类加载之前进行一些数据的处理,对需要参数或对象的初始化。保证接下来对该参数或对象的使用。

 

这个例子就大致展现出了静态代码块的一些基本的作用,另外我们可以看看这个例子,展现了静态代码块和对象初始化时的加载顺序:

 

 

  4、 静态导包

静态导包是一种比较少见是static使用方式,接下来使用一个例子来说明一下吧:

 

 

如图,当我们使用import static staticpkg.Man.*;时,在不与本类方法名冲突的前提下,我们可以在本类中直接使用Man类中的static方法,就像是本类自己的方法一样。但是相应的其他静态参数和一下非静态参数和非静态方法就无法直接调用了。

 

5、 静态内部类

 

首先简单描述一下什么是内部类,Java中是允许在类的构建中定义另一个类的,内部类有很多优点,首先就是不能被同一包的其他类所见,具有很好的封装性,其次还有就是内部类对象可以访问创建它的对象的实现,包括私有数据等;

 

还是举个例子说明一下:

 

 

 

输出结果为:

 

 

从例子中的注释我们可以看到内部类有许多的特性:

 

1、 静态内部类中可以存在静态成员,而非静态内部类中不能有静态成员。

2、 静态内部类可以访问外部类的静态成员,而非静态内部类则可以访问外部类的所有成员。

3、 如果需要在其他类中实例化本类中的非静态内部类,需要先修改非静态内部类的访问限制符,然后实例化外部类,再通过外部类对象实例化非静态内部类。

4、 如果需要在其他类中实例化本类中的静态内部类,需要先修改静态内部类的访问限制符,然后直接通过new 该静态内部类就可以了。

5、 调用静态内部类的静态成员时,可以通过外部类名.内部静态类名.静态参数名称或静态方法名。

 

二、 static作用分析

 

1、 之前说了static是跟随着类进行加载的,那么我们是否能通过this来进行静态参数的调用呢?

 

 

我们可以看到虽然静态参数是随着类进行加载的,但是在参数名有冲突的情况下依然可以通过this来进行参数调用,以此区分全局变量和局部变量的不同。

 

2、 static是跟跟随着类进行加载的,那么伴随着类、基础、对象的初始化过程中相应的加载顺序是怎样的?

举例1

 

 

 

 

这两段代码的输出结果会是怎样的?

我们说了static是跟着类一起进行加载的,那么我们在new Test3()时会加载Test3类这时发现Test3继承Test2那么我们需要先加载Test2类的静态代码块,再加载Test3类的静态代码块。类加载完成后我们开始通过Test3提供的构造器来今天Test3对象的初始化,而因为Test3继承了Test2所以在编译时默认Test3的构造器中有一个super()的调用,意思就是先加载Test2的构造器再加载Test3的构造器。所以这两段的代码的输出结果如下:

 

 

举例2

 

 

 

 

 

 

这三段代码的加载顺序又应该是怎样的呢?

首先和举例1一样main函数中加载Test3类,但是因为继承Test2所以先加载Test2类的静态代码块,然后是Test3的静态代码块,然后开始加载Test3的构造函数了,同样因为继承需要先加载Test2的构造函数,但是需要初始化Test2对象时需要将Test2类中的全局变量进行逐行加载,这时我们就需要加载new Test4(“test22222222”),这时就需要加载Test4类了,执行Test4类的静态代码块,然后执行Test4的构造器,Test4对象加载完成,进行Test2类的构造器加载,然后到了Test3类构造器加载,同上需要先加载Test4类,因为Test4类的静态代码块已经加载完成无需重复加载,所以加载Test4的构造器,然后加载Test3的构造器,所以这三段代码的输出结果应为:

 

 

举例3

 

 

Main函数中明明什么都没写为什么还是会打印呢?

因为在加载main函数时,会先对Test5类进行逐行加载,这时会从上到下依次加载两个静态代码块,加载完成后,本类加载完成开始执行main函数中的代码,结果main函数中没有代码,程序结束。

 

以上是我对static的一些基本的认知,希望能对你有所帮助,代码依然是图片,代码还是手敲一遍试一下更有感觉的。

 

参考博客:

https://www.cnblogs.com/dotgua/p/6354151.html?utm_source=itdadao&utm_medium=referral

http://www.cnblogs.com/dolphin0520/p/3799052.html

https://www.cnblogs.com/aademeng/articles/6192954.html

 

以上是关于Java static基本认知的主要内容,如果未能解决你的问题,请参考以下文章

java中static怎么用

java基础 static

Java高阶语法---static

java中static与final关键字归纳总结

java之关键字static

Java static和final