• Java 静态、类加载


    1.静态是什么?有什么用?

    static的主要作用在于创建独立于具体对象的域变量或者方法。

    静态代码在程序运行之前,即编译阶段,分配内存。

    每创建一个对象,都会在堆里开辟内存,存成员(属性),但是不存方法,方法是共用的,没必要每一个对象都浪费内存去存方法。有一个叫方法区的地方存方法。方法区里又有静态域,存静态变量或者静态方法。

    普通变量和方法的调用:通过对象调用

    静态变量和方法的调用:通过对象或类调用

    public class MyTest8 {
        public static void main(String[] args) {
            System.out.println(Plant.name);//通过类调用
            Plant p1=new Plant();
            System.out.println(p1.name);//通过对象调用
            p1.name="植物";//类似方法,是公用的,改了之后就是改了,新建对象也是这个值
            Plant p2=new Plant();
            System.out.println(p2.name);//通过对象调用
        }
    }
    
    class Plant{
        static String name="静态植物";
        public static void say() {
            System.out.println("植物类");
        }
    }
    /**输出
    静态植物
    静态植物
    植物
    */
    静态的调用

     在类被加载的时候,静态的代码都会被加载,并且只加载一次,放在方法区里,该类的对象一起用。省时省力。

    2.加载类有什么用?

    任何程序都要先加载到内存中才能和CPU进行交流,而JVM中的ClassLoader(类加载器)就是负责提前将,class文件加载到内存中去的。

    3.类何时被加载?(普通人的宏观理解)

    • 实例化对象时,如Chinese c1=new Chinese();此时加载了Chinese类
    • 通过类名调用静态变量或静态方法的时候
    • 如果实例化子类对象,会先加载父类

    通过代码验证:

    public class MyTest8 {
    
        public static void main(String[] args) {
            Plant.say();//通过类名调用静态方法
            Plant p1 = new Plant();//创建对象
            Plant p2 = new Plant();
        }
    }
    
    class Plant{
        static String name="静态植物";
        public static void say() {
            System.out.println("植物类");
        }
        static {
            System.out.println("植物类的静态代码被执行加载了");
        }
        {
            System.out.println("植物类的普通代码被加载了");
        }
    }

    输出:

    植物类的静态代码被执行加载了
    植物类
    植物类的普通代码被加载了
    植物类的普通代码被加载了

    由此可知,Plant.say();调用方法时就加载了类,静态代码也都被执行了,并且只执行一次,创建对象的时候再次加载类,但是不执行静态代码,而是非静态代码。

    再通过继承关系观察静态和非静态的关系

    public class MyTest8 {
    
        public static void main(String[] args) {
            Flower.say();//通过类名调用静态方法
            Flower f1=new Flower();//创建花类对象
            Flower f2=new Flower();
        }
    }
    
    class Plant{
        static String name="静态植物";
        public static void say() {
            System.out.println("植物类");
        }
        static {
            System.out.println("植物类的静态代码被执行加载了");
        }
        {
            System.out.println("植物类的普通代码被加载了");
        }
    }
    
    class Flower extends Plant{
        static {
            System.out.println("花类的普通代码被加载了");
        }
        
        static String name="花";
        public static void say() {
            System.out.println("花类");
        }
        {
            System.out.println("花类的普通代码被加载了");
        }
    }

    输出:

    植物类的静态代码被执行加载了
    花类的普通代码被加载了
    花类
    植物类的普通代码被加载了
    花类的普通代码被加载了
    植物类的普通代码被加载了
    花类的普通代码被加载了

    由此可知:调用静态的东西,只执行静态的代码,静态代码只执行一次。创建对象的时候也会加载,如果静态没有被加载过也会被加载,加载过就不用。加载子类的时候,会先把父类加载一次。

    在代码中,调用子类静态方法,则先加载父类的静态代码,再加载子类的静态代码,再调用方法。创建对象的时候,先加载父类,再加载子类。

    类装载的过程:

    • 加载:根据查找路径找到相应的 class 文件然后导入;
    • 检查:检查加载的 class 文件的正确性;
    • 准备:给类中的静态变量分配内存空间;
    • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
    • 初始化:对静态变量和静态代码块执行初始化工作。

    牛客刷题

    1.初始化代码执行顺序是:

    父类静态变量→父类静态代码块→子类静态变量→子类静态代码块→

    父类普通变量→父类普通代码块→父类构造方法→

    子类普通变量→子类普通代码块→子类构造方法

    严格的说,静态变量+静态代码块=静态域,不包括静态方法,谁先谁后根据在类中的位置,由于一般都是先声明变量的,所以有这样的总结。这里有一个难以想象的题目,十有八九会被坑,详看代码及注释,节省篇幅。

    public class B {
        public static B t1 = new B();
        public static B t2 = new B();
        {
            System.out.println("构造块");
        }
        static
        {
            System.out.println("静态块");
        }
        public static void main(String[] args)
        {
            B t = new B();
        }
    }
    
    
    /**
     输出:
     构造块
     构造块
     静态块
     构造块
     --------------------------------------------------
     解释:
     1.加载B.class
     2.按照顺序先对t1和t2进行初始化,默认为null
     3.又需要对t1和t2进行显示初始化,所以需要加载默认的构造方法和普通代码块
     4.为什么不会加载静态代码块呢?
     5.因为刚开始加载B.class时就算是加载了静态域,现在还没加载完,又不能重复加载,所以没有在初始化t1的时候输出静态块
     6.t1和t2加载完了按顺序就轮到静态代码块了
     7.走main方法,又创建一次B对象,执行构造方法和普通代码块
     
     要点:静态域只加载一次,不能重复加载
     */
    View Code

    2.静态方法属于类不属于对象;静态方法中只能访问其他静态方法和静态数据,不能访问非静态的。

    3.静态变量建议别用this调用

    在语法上行得通,但是静态变量是所有实例共享的一个变量,而this通常指当前对象;用对象名调用普通变量,用类名调用静态变量更加直观。很多题目直接把用this调用的情况全部判定为错。

  • 相关阅读:
    JS 日期加多少天,减多少天
    SQL 触发器
    SGU100
    连续子数组的最大和
    字符串的排列
    二叉搜索树与双向链表
    数组中出现次数超过一半的数字
    复杂链表的复制
    二叉树中和为某一值的路径
    二叉搜索树的后序遍历序列
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/11570159.html
Copyright © 2020-2023  润新知