美文网首页
Java的static关键字

Java的static关键字

作者: Atomic | 来源:发表于2023-08-10 11:43 被阅读0次

    Java关键字是什么?

    摘自百度百科

    Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。

    常用的关键字如下图


    Java关键字.png

    针对于上述关键字,我们抽出几个比较重要的来唠唠

    修饰字符类型关键字

    static

    在Java中,static表示“静态的”,它也是一种修饰符,可以修饰属性、方法、代码块和内部类。
    一般情况下,static修饰的变量,方法,代码块。都跟随类的生命周期,它们所属的JVM内存也都是处于共享内存

    变量:

    静态变量(或称为类变量),指被 static 修饰的成员变量;类的静态变量在内存中只有一个,Java虚拟机在加载类的过程中为静态变量分配内存,静态变量位于方法区,被类的所有实例共享。静态变量可以直接通过类名被访问。静态变量的生命周期取决于类的生命周期,当加载类的时候,静态变量被创建并分配内存,当卸载类的时候,静态变量被销毁并撤销内存。
    实例变量,指没有被 static 修饰的成员变量;类的每个实例都有相应的实例变量。每创建一个类的实例,Java虚拟机就会为实例变量分配一次内存,实例变量位于堆区中。实例变量的生命周期取决于实例的生命周期,当创建实例的时候,实例变量被创建并分配内存,当销毁实例的时候,实例变量被销毁并撤销内存。
    局部变量,指的是在一个方法的内部或者方法的一个代码块中声明的变量,前者的作用域就是整个方法,后者的作用域则是这个代码块(代码块就是{}以内的代码)

    class Person() {
      private String name; // 实例变量
      private Integer age; // 实例变量
      private static String country = "China"; // 静态变量
    
      public person(String name, Integer age) {
        this.name = name;
        this.age = age;
      }
    }
    

    当我们使用这个类创建实例的时候

      public static void main(String[] args) {
        Person p1 = new Person("zhangfei", 30);
        Person p2 = new Person("guanyu", 35);
        
        System.out.println(p1.name);
        System.out.println(Person.country);
      }
    

    内存分配如下:


    Java_static内存分配.jpg
    方法:
    • 静态方法是使用 static 关键字修饰的成员方法,它属于类,而不是类的实例。
    • 静态方法可以直接通过类名来调用,也可以通过类的对象来调用,但一般推荐使用类名来调用。
    • 静态方法只能访问类的静态变量和静态方法,不能访问类的实例变量和实例方法,也不能使用 this 关键字。
    • 静态方法通常是一些工具方法,不需要依赖于对象的状态,例如 Collections 类、Math 类等都包含了很多静态方法。
    # 接上面的Person类
    class Person() {
      ……
    
      public String printName() {
        System.out.println(this.name);
      }
    
      public static printCountry() {
        System.out.println(Person.country);
      }
      
    }
    

    实际调用

      public static void main(String[] args) {
        Person p1 = new Person("zhangfei", 30);
        Person p2 = new Person("guanyu", 35);
        
        p1.printName();
        Person.printCountry(); // 使用类名调用
        p1.printName(); // 使用对象调用,推荐使用类名调用
      }
    
    代码块:

    静态代码块,在Java类中,使用一对大括号包围起来的若干行代码被称为一个代码块,用static关键字修饰的代码块称为静态代码块。
    当类被加载时,静态代码块会执行,由于类只加载一次,所以静态代码块只会执行一次。在程序中,通常会使用静态代码块来对类的成员变量进行初始化

    # 接上面的Person类
    class Person() {
      static{
        System.out.println("开始加载类,执行静态代码块");
      }
    }
    

    构造代码块
    java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)

    # 接上面的Person类
    class Person() {
      {
        System.out.println("开始加载类,执行构造代码块");
      }
    }
    

    这里要梳理清楚执行顺序

    静态代码块>构造代码块>构造函数>普通代码块

    我们可以实际模拟运行一下

    public class codeBlock {
        static {
            System.out.println("静态代码块");
        }
        {
            System.out.println("构造代码块");
        }
        public codeBlock(){
            System.out.println("无参构造函数");
        }
        public void sayHello(){
            System.out.println("普通代码块");
        }
    
        public static void main(String[] args) {
            System.out.println("执行了main方法");
            new codeBlock().sayHello();
            System.out.println("---------------------------");
        }
    }
    

    执行结果如下:

    静态代码块
    执行了main方法
    构造代码块
    无参构造函数
    普通代码块
    ---------------------------
    

    嵌套类

    嵌套类又成为内部类,内部类就是一个定义在一个类里面的类,里面的类可以理解为(寄生),外部类可以理解成(宿主)
    分为静态内部类和非静态内部类;

    • 静态内部类是指被声明为static的内部类,
    • 非静态内部类是指没有被声明为static的内部类

    实例化:静态内部类的实例化不依赖于外部类的实例,可以直接通过类名访问;而非静态内部类的实例化必须依赖于外部类的实例,只能在外部类的实例方法中创建。
    关系:静态内部类与外部类没有任何联系,只是被包含在外部类中;而非静态内部类可以访问外部类的成员和方法,并且可以使用外部类的引用来访问外部类的成员。

    静态内部类不需要依赖外部类的实例而可以被实例化。静态内部类通常用于封装与外部类紧密相关的一组静态方法或常量

    public class OuterClass {
        public static class InnerClass {
            // 静态内部类中可以定义一组静态方法或常量
            public static final int MAX_VALUE = 100;
            public static int add(int x, int y) {
                return x + y;
            }
        }
    }
    
    public class OuterClass {
        private int outerVar;
        
        public OuterClass(int outerVar) {
            this.outerVar = outerVar;
        }
        
        public void outerMethod() {
            int localVar = 10;
            // 非静态内部类
            class InnerClass {
                public void innerMethod() {
                    System.out.println(outerVar); // 访问外部类的实例变量
                    System.out.println(localVar); // 访问局部变量
                }
            }
            
            InnerClass inner = new InnerClass();
            inner.innerMethod();
            
            // 静态内部类
            static class StaticInnerClass {
                public static final int MAX_VALUE = 100;
                public static void staticInnerMethod() {
                    System.out.println("This is a static inner class.");
                }
            }
            
            StaticInnerClass staticInner = new StaticInnerClass();
            staticInner.staticInnerMethod();
        }
    }
    

    非静态内部类:InnerClass可以访问外部类的实例变量outerVar和局部变量localVar,因此适合在需要访问外部类实例的情况下使用**,例如实现一个事件监听器。

    Outer.Inner in = new Outer().new.Inner()

    静态内部类:StaticInnerClass没有访问外部类实例的需求,只需要实现一些独立的功能,因此适合在不需要访问外部类实例的情况下使用,例如实现一个工具类。同时,静态内部类的实例化不依赖于外部类的实例,可以直接通过类名访问,使用起来更加方便。和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已

    Outer.Inner in = new Outer.Inner();

    一道有意思的面试题

    class People() {
      private int heartbeat = 150;
      public class Heart {
        private int heartbeat = 110;
        public void show() {
          int heartbeat = 80;
          System.out.println(heartbeat);//80
          System.out.println(this.heartbeat);//110
          System.out.println(People.this.heartbeat);//150
        }
      }
    
      public static void main(String[] args) {
        new People().new Heart().show();
      }
    }
    

    输出结果

    80
    110
    150
    

    匿名内部类(重要)
    匿名内部类可以使你的代码更加简洁,你可以在定义一个类的同时对其进行实例化。它与局部类很相似,不同的是它没有类名,如果某个局部类你只需要用一次,那么你就可以使用匿名内部类
    但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

    匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们。
    匿名类的声明是在编译时进行的,实例化在运行时进行,这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。

    为什么在Java中需要内部类?
    我们先看一下不使用匿名内部类来实现抽象方法的时候

    abstract class Person {
        public abstract void eat();
    }
     
    class Child extends Person {
        public void eat() {
            System.out.println("eat something");
        }
    }
     
    public class Demo {
        public static void main(String[] args) {
            Person p = new Child();
            p.eat();
        }
    }
    

    用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
    但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
    这个时候就引入了匿名内部类

    引入匿名内部类之后

    abstract class Person {
        public abstract void eat();
    }
     
    public class Demo {
        public static void main(String[] args) {
            Person p = new Person() {
                public void eat() {
                    System.out.println("eat something");
                }
            };
            p.eat();
        }
    }
    

    可以看到,我们直接将抽象类Person中的方法在大括号中实现了
    这样便可以省略一个类的书写,并且,匿名内部类还能用于接口上

    interface Person {
        public void eat();
    }
     
    public class Demo {
        public static void main(String[] args) {
            Person p = new Person() {
                public void eat() {
                    System.out.println("eat something");
                }
            };
            p.eat();
        }
    }
    

    只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
    最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口

    Thread类的匿名内部类实现

    public class Demo {
        public static void main(String[] args) {
            Thread t = new Thread() {
                public void run() {
                    for (int i = 1; i <= 5; i++) {
                        System.out.print(i + " ");
                    }
                }
            };
            t.start();
        }
    }
    

    Runnable接口的匿名内部类实现

    public class Demo {
        public static void main(String[] args) {
            Runnable r = new Runnable() {
                public void run() {
                    for (int i = 1; i <= 5; i++) {
                        System.out.print(i + " ");
                    }
                }
            };
            Thread t = new Thread(r);
            t.start();
        }
    }
    

    这篇文章介绍匿名内部类使用场景,比较通俗易懂。java匿名内部类总结

    匿名内部类总结:
    1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
    2、匿名内部类中是不能定义构造函数的。
    3、匿名内部类中不能存在任何的静态成员变量和静态方法。
    4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
    5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

    相关文章

      网友评论

          本文标题:Java的static关键字

          本文链接:https://www.haomeiwen.com/subject/zloopdtx.html