51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

我说Java完全面向对象,老大过来就是一jio-20210825

哈喽,大家好,我是指北君。
自从开始学Java,就知道Java是一门面向对象编程的语言,所以在指北君眼中,Java就是完全面向对象的。
有一天老大问到我这个事情,我脱口而出,结果老大过来就是一jio......

老大问我说static关键词是面向对象的吗?没有对象,能不能调用static关键词修饰的方法,变量,我瞬间哑口无言。
于是,我绞尽脑汁,写下了这篇文章,彻底搞懂了 static 关键词的用法。

static 是Java的一个关键字,可以用来修饰成员变量、修饰成员方法、构造静态代码块、实现静态导包以及实现静态内部类,下面我们来分别介绍。

1、修饰成员变量 {#1修饰成员变量}

  用 static 修饰成员变量可以说是该关键字最常用的一个功能,通常将用 static 修饰的成员变量称为类成员或者静态成员,那么静态成员和不用 static 修饰的非静态成员有什么区别呢?

  我们先看看不用 static 修饰的成员变量在内存中的构造。

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  这两个对象在内存中的存储结构如下:

  由上图可知,我们创建的两个对象 p1 和 p2 存储在堆中,但是其引用地址是存放在栈中的,而且这两个对象的两个变量互相独立,我们修改任何一个对象的属性值,是不改变另外一个对象的属性值的。

  下面我们将 Person 类中的 age 属性改为由 static 关键字修饰:

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by YSOcean */ public class Person { private String name; private static Integer age;

    public Person(String name, Integer age) { this.name = name; this.age = age; }

    @Override public String toString() { return "Person{" + "name='" + name + '

  同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4</pre></td>
                  <td class="code"><pre class="highlight"><code>Person p1 = new Person("Tom",21);

Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=20} System.out.println(p2.toString());//Person{name='Marry', age=20}

                </tbody>
               
               </table>
              
              </div>
             
             </div>

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?

  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法 {#2修饰修饰成员方法}

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22</pre></td>
                  <td class="code"><pre class="highlight"><code>public class Person {
private  String name;
private static Integer age;

public static void printClassName(){
    System.out.println("com.ys.bean.Person");
}
public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '

  调用静态方法:

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1</pre></td>
                          <td class="code"><pre class="highlight"><code>Person.printClassName();//com.ys.bean.Person


                        </tbody>
                       
                       </table>
                      
                      </div>
                     
                     </div>

3、静态代码块 {#3静态代码块}

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5</pre></td>
                          <td class="code"><pre class="highlight"><code>public class CodeBlock {
static{
    System.out.println("静态代码块");
}

}

                        </tbody>
                       
                       </table>
                      
                      </div>
                     
                     </div>

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包 {#4静态导包}

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
                          <td class="code"><pre class="highlight"><code>package com.ys.test;

import java.util.Arrays; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; Arrays.sort(arrays); } }

                         </tbody>
    
                        </table>
    
                       </div>
    
                      </div>
    

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
                          <td class="code"><pre class="highlight"><code>package com.ys.test;

import static java.util.Arrays.*; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; sort(arrays); } }

                         </tbody>
    
                        </table>
    
                       </div>
    
                      </div>
    

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类 {#5静态内部类}

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
                          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public class InnerClass{

    } }

                         </tbody>
    
                        </table>
    
                       </div>
    
                      </div>
    
    
                      <div class="language-plaintext highlighter-rouge">
    
                       <div class="highlight">
    
                        <table style="margin: 0px">
    
                         <tbody>
    
                          <tr>
                           <td class="gutter"><pre>1<br>2</pre></td>
                           <td class="code"><pre class="highlight"><code>  * OuterClass oc = new OuterClass();
    
  • OuterClass.InnerClass in = oc.new InnerClass();

                        </tbody>
    
                       </table>
    
                      </div>
    
                     </div>
    

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

                     <div class="language-plaintext highlighter-rouge">
                      
                      <div class="highlight">
                       
                       <table style="margin: 0px">
                        
                        <tbody>
                         
                         <tr>
                          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
                          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public static class InnerClass{

    } }

                         </tbody>
    
                        </table>
    
                       </div>
    
                      </div>
    
    
                      <div class="language-plaintext highlighter-rouge">
    
                       <div class="highlight">
    
                        <table style="margin: 0px">
    
                         <tbody>
    
                          <tr>
                           <td class="gutter"><pre>1</pre></td>
                           <td class="code"><pre class="highlight"><code>OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();
    
    
                          </tbody>
    
                         </table>
    
                        </div>
    
                       </div>
    

6、常见问题 {#6常见问题}

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

7、小结 {#7小结}

  好了,这就是Java中对 static 关键词的介绍,这下大家都清楚了吧,面向对象的漏网之鱼。

  指北君后续的文章会给大家介绍Java中的各种关键字。

  我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

                </tbody>
               
               </table>
              
              </div>
             
             </div>

  首先,我们创建一个实体类 Person,有两个属性 name 和 age,都是普通成员变量(没有用 static 关键字修饰),接着我们通过其构造方法创建两个对象:

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4</pre></td>
                  <td class="code"><pre class="highlight"><code>Person p1 = new Person("Tom",21);

Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=21} System.out.println(p2.toString());//Person{name='Marry', age=20}

                </tbody>
               
               </table>
              
              </div>
             
             </div>

  这两个对象在内存中的存储结构如下:

  由上图可知,我们创建的两个对象 p1 和 p2 存储在堆中,但是其引用地址是存放在栈中的,而且这两个对象的两个变量互相独立,我们修改任何一个对象的属性值,是不改变另外一个对象的属性值的。

  下面我们将 Person 类中的 age 属性改为由 static 关键字修饰:

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <pre class="highlight"><code>package com.ys.bean;

/**

  • Create by YSOcean */ public class Person { private String name; private static Integer age;

    public Person(String name, Integer age) { this.name = name; this.age = age; }

    @Override public String toString() { return "Person{" + "name='" + name + ''' + ", age=" + age + '}'; } //get和set方法省略

}

              </div>
             
             </div>

  同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4</pre></td>
                  <td class="code"><pre class="highlight"><code>Person p1 = new Person("Tom",21);

Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=20} System.out.println(p2.toString());//Person{name='Marry', age=20}

                </tbody>
               
               </table>
              
              </div>
             
             </div>

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?

  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法 {#2修饰修饰成员方法}

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <pre class="highlight"><code>public class Person {
private  String name;
private static Integer age;

public static void printClassName(){
    System.out.println("com.ys.bean.Person");
}
public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
//get和set方法省略

}

              </div>
             
             </div>

  调用静态方法:

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1</pre></td>
                  <td class="code"><pre class="highlight"><code>Person.printClassName();//com.ys.bean.Person


                </tbody>
               
               </table>
              
              </div>
             
             </div>

3、静态代码块 {#3静态代码块}

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5</pre></td>
                  <td class="code"><pre class="highlight"><code>public class CodeBlock {
static{
    System.out.println("静态代码块");
}

}

                </tbody>
               
               </table>
              
              </div>
             
             </div>

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包 {#4静态导包}

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
                  <td class="code"><pre class="highlight"><code>package com.ys.test;

import java.util.Arrays; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; Arrays.sort(arrays); } }

                 </tbody>
    
                </table>
    
               </div>
    
              </div>
    

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
                  <td class="code"><pre class="highlight"><code>package com.ys.test;

import static java.util.Arrays.*; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; sort(arrays); } }

                 </tbody>
    
                </table>
    
               </div>
    
              </div>
    

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类 {#5静态内部类}

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
                  <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public class InnerClass{

    } }

                 </tbody>
    
                </table>
    
               </div>
    
              </div>
    
    
              <div class="language-plaintext highlighter-rouge">
    
               <div class="highlight">
    
                <table style="margin: 0px">
    
                 <tbody>
    
                  <tr>
                   <td class="gutter"><pre>1<br>2</pre></td>
                   <td class="code"><pre class="highlight"><code>  * OuterClass oc = new OuterClass();
    
  • OuterClass.InnerClass in = oc.new InnerClass();

                </tbody>
    
               </table>
    
              </div>
    
             </div>
    

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

             <div class="language-plaintext highlighter-rouge">
              
              <div class="highlight">
               
               <table style="margin: 0px">
                
                <tbody>
                 
                 <tr>
                  <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
                  <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public static class InnerClass{

    } }

                 </tbody>
    
                </table>
    
               </div>
    
              </div>
    
    
              <div class="language-plaintext highlighter-rouge">
    
               <div class="highlight">
    
                <table style="margin: 0px">
    
                 <tbody>
    
                  <tr>
                   <td class="gutter"><pre>1</pre></td>
                   <td class="code"><pre class="highlight"><code>OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();
    
    
                  </tbody>
    
                 </table>
    
                </div>
    
               </div>
    

6、常见问题 {#6常见问题}

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

7、小结 {#7小结}

  好了,这就是Java中对 static 关键词的介绍,这下大家都清楚了吧,面向对象的漏网之鱼。

  指北君后续的文章会给大家介绍Java中的各种关键字。

  我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

}

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4</pre></td>
          <td class="code"><pre class="highlight"><code>Person p1 = new Person("Tom",21);

Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=20} System.out.println(p2.toString());//Person{name='Marry', age=20}

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?

  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法 {#2修饰修饰成员方法}

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <pre class="highlight"><code>public class Person {
private  String name;
private static Integer age;

public static void printClassName(){
    System.out.println("com.ys.bean.Person");
}
public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
//get和set方法省略

}

      </div>
     
     </div>

  调用静态方法:

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1</pre></td>
          <td class="code"><pre class="highlight"><code>Person.printClassName();//com.ys.bean.Person


        </tbody>
       
       </table>
      
      </div>
     
     </div>

3、静态代码块 {#3静态代码块}

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5</pre></td>
          <td class="code"><pre class="highlight"><code>public class CodeBlock {
static{
    System.out.println("静态代码块");
}

}

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包 {#4静态导包}

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.test;

import java.util.Arrays; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; Arrays.sort(arrays); } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.test;

import static java.util.Arrays.*; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; sort(arrays); } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类 {#5静态内部类}

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public class InnerClass{

    } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    
    
      <div class="language-plaintext highlighter-rouge">
    
       <div class="highlight">
    
        <table style="margin: 0px">
    
         <tbody>
    
          <tr>
           <td class="gutter"><pre>1<br>2</pre></td>
           <td class="code"><pre class="highlight"><code>  * OuterClass oc = new OuterClass();
    
  • OuterClass.InnerClass in = oc.new InnerClass();

        </tbody>
    
       </table>
    
      </div>
    
     </div>
    

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public static class InnerClass{

    } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    
    
      <div class="language-plaintext highlighter-rouge">
    
       <div class="highlight">
    
        <table style="margin: 0px">
    
         <tbody>
    
          <tr>
           <td class="gutter"><pre>1</pre></td>
           <td class="code"><pre class="highlight"><code>OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();
    
    
          </tbody>
    
         </table>
    
        </div>
    
       </div>
    

6、常见问题 {#6常见问题}

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

7、小结 {#7小结}

  好了,这就是Java中对 static 关键词的介绍,这下大家都清楚了吧,面向对象的漏网之鱼。

  指北君后续的文章会给大家介绍Java中的各种关键字。

  我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.ys.bean; /** * Create by YSOcean */ public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '   首先,我们创建一个实体类 Person,有两个属性 name 和 age,都是普通成员变量(没有用 static 关键字修饰),接着我们通过其构造方法创建两个对象:

1
2
3
4
Person p1 = new Person("Tom",21); Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=21} System.out.println(p2.toString());//Person{name='Marry', age=20} |

  首先,我们创建一个实体类 Person,有两个属性 name 和 age,都是普通成员变量(没有用 static 关键字修饰),接着我们通过其构造方法创建两个对象:

|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 | Person p1 = new Person("Tom",21); Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=21} System.out.println(p2.toString());//Person{name='Marry', age=20} |

  这两个对象在内存中的存储结构如下:


  由上图可知,我们创建的两个对象 p1 和 p2 存储在堆中,但是其引用地址是存放在栈中的,而且这两个对象的两个变量互相独立,我们修改任何一个对象的属性值,是不改变另外一个对象的属性值的。

  下面我们将 Person 类中的 age 属性改为由 static 关键字修饰:

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?

  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法 {#2修饰修饰成员方法}

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <pre class="highlight"><code>public class Person {
private  String name;
private static Integer age;

public static void printClassName(){
    System.out.println("com.ys.bean.Person");
}
public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}

@Override
public String toString() {
    return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
//get和set方法省略

}

      </div>
     
     </div>

  调用静态方法:

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1</pre></td>
          <td class="code"><pre class="highlight"><code>Person.printClassName();//com.ys.bean.Person


        </tbody>
       
       </table>
      
      </div>
     
     </div>

3、静态代码块 {#3静态代码块}

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5</pre></td>
          <td class="code"><pre class="highlight"><code>public class CodeBlock {
static{
    System.out.println("静态代码块");
}

}

        </tbody>
       
       </table>
      
      </div>
     
     </div>

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包 {#4静态导包}

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.test;

import java.util.Arrays; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; Arrays.sort(arrays); } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11<br>12<br>13</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.test;

import static java.util.Arrays.*; /**

  • Create by YSOcean */ public class StaticTest {

    public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; sort(arrays); } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类 {#5静态内部类}

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public class InnerClass{

    } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    
    
      <div class="language-plaintext highlighter-rouge">
    
       <div class="highlight">
    
        <table style="margin: 0px">
    
         <tbody>
    
          <tr>
           <td class="gutter"><pre>1<br>2</pre></td>
           <td class="code"><pre class="highlight"><code>  * OuterClass oc = new OuterClass();
    
  • OuterClass.InnerClass in = oc.new InnerClass();

        </tbody>
    
       </table>
    
      </div>
    
     </div>
    

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

     <div class="language-plaintext highlighter-rouge">
      
      <div class="highlight">
       
       <table style="margin: 0px">
        
        <tbody>
         
         <tr>
          <td class="gutter"><pre>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>10<br>11</pre></td>
          <td class="code"><pre class="highlight"><code>package com.ys.bean;

/**

  • Create by hadoop */ public class OutClass {

    public static class InnerClass{

    } }

         </tbody>
    
        </table>
    
       </div>
    
      </div>
    
    
      <div class="language-plaintext highlighter-rouge">
    
       <div class="highlight">
    
        <table style="margin: 0px">
    
         <tbody>
    
          <tr>
           <td class="gutter"><pre>1</pre></td>
           <td class="code"><pre class="highlight"><code>OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();
    
    
          </tbody>
    
         </table>
    
        </div>
    
       </div>
    

6、常见问题 {#6常见问题}

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

7、小结 {#7小结}

  好了,这就是Java中对 static 关键词的介绍,这下大家都清楚了吧,面向对象的漏网之鱼。

  指北君后续的文章会给大家介绍Java中的各种关键字。

  我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

}


|------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package com.ys.bean; /** * Create by YSOcean */ public class Person { private String name; private static Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '   同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

1
2
3
4
Person p1 = new Person("Tom",21); Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=20} System.out.println(p2.toString());//Person{name='Marry', age=20} |

  同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 | Person p1 = new Person("Tom",21); Person p2 = new Person("Marry",20); System.out.println(p1.toString());//Person{name='Tom', age=20} System.out.println(p2.toString());//Person{name='Marry', age=20} |

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?


  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法 {#2修饰修饰成员方法}

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

public class Person {
    private  String name;
    private static Integer age;

    public static void printClassName(){
        System.out.println("com.ys.bean.Person");
    }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //get和set方法省略



`}
`

  调用静态方法:

|-----------|----------------------------------------------| | 1 | Person.printClassName();//com.ys.bean.Person |

3、静态代码块 {#3静态代码块}

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

|-------------------|-------------------------------------------------------------------| | 1 2 3 4 5 | public class CodeBlock { static{ System.out.println("静态代码块"); } } |

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包 {#4静态导包}

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.ys.test; import java.util.Arrays; /** * Create by YSOcean */ public class StaticTest { public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; Arrays.sort(arrays); } } |

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.ys.test; import static java.util.Arrays.*; /** * Create by YSOcean */ public class StaticTest { public static void main(String[] args) { int[] arrays = {3,4,2,8,1,9}; sort(arrays); } } |

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类 {#5静态内部类}

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

|---------------------------------|-------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 | package com.ys.bean; /** * Create by hadoop */ public class OutClass { public class InnerClass{ } } |

|-------------|---------------------------------------------------------------------------------------| | 1 2 | * OuterClass oc = new OuterClass(); * OuterClass.InnerClass in = oc.new InnerClass(); |

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 | package com.ys.bean; /** * Create by hadoop */ public class OutClass { public static class InnerClass{ } } |

|-----------|----------------------------------------------------------------------| | 1 | OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass(); |

6、常见问题 {#6常见问题}

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

7、小结 {#7小结}

  好了,这就是Java中对 static 关键词的介绍,这下大家都清楚了吧,面向对象的漏网之鱼。
  指北君后续的文章会给大家介绍Java中的各种关键字。

  我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩! ' + ", age=" + age + '}'; } //get和set方法省略 }

赞(1)
未经允许不得转载:工具盒子 » 我说Java完全面向对象,老大过来就是一jio-20210825