static静态关键字详解(工具类、代码块、单例模式)_static关键字-程序员宅基地

技术标签: JavaSE  jvm  java  开发语言  

一.static静态关键字

1.static是什么,static修饰成员变量的用法

  • static是静态的意思,可以用来修饰成员变量、成员方法。
  • static修饰成员变量之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)。
  • static修饰后的成员变量,可以被类的所有对象共享(访问、修改)
  • 静态成员变量(有static修饰,属于类,加载一次,内存中只有一份),访问格式:
  1. 类名.静态成员变量(推荐)
  2. 对象.静态成员变量(不推荐)
  • 实例成员变量(无static修饰,属于对象,每个对象中都存储1份),访问格式:
  1. 对象.实例成员变量
  • 静态成员变量:表示在线人数等需要被类的所有对象共享的信息时。
  • 实例成员变量:属于每个对象,且每个对象的信息不同时(如:name、age....)
package com.gch.d1static;

public class User {
    /**
        在线人数
        注意:static修饰的成员变量,静态成员变量,只在内存中存储1份,可以被所有对象共享
     */

    // 静态成员变量
    public static int onlineNumber = 161;

    // 实例成员变量:无static修饰的,属于每个对象的,必须使用对象名.访问
    private String name;
    private int age;

    public static void main(String[] args) {
        // 目标:理解static修饰成员变量的作用和访问特点
        // 1. 类名.静态成员变量
        System.out.println(User.onlineNumber); // 161

        // 2.对象名.静态成员变量
    //    System.out.println(User.name); 报错
        User u = new User();
        u.name = "张三";
        u.age = 18;
        System.out.println(u.name);
        System.out.println(u.age);


        u.onlineNumber++; // 新进来了一个人
        System.out.println(u.onlineNumber); // 162

        // 注意:同一个类中静态成员变量的访问可以省略类名
        System.out.println(onlineNumber); // 162
    }
}

 2.static修饰成员变量的内存原理

  • 实例成员变量属于每一个对象,对象在堆内存中。
  • 当这个类加载到方法区的同时,它会同步的在堆内存中开辟一块儿这个类的静态变量区,并且只加载一次。
  • 同一个类中,访问静态成员变量,类名可以省略不写。
  • 同一个类中,访问静态成员方法,类名可以省略不写。 

 3.static修饰成员方法的用法

 成员方法的分类:

  • 静态成员方法(有static修饰,归属于类,属于类和对象共享):建议用类名访问,也可以用对象访问。
  • 实例成员方法(无static修饰,归属于对象):只能用对象触发访问。
  • 同一个类中,访问静态成员方法,类名可以省略不写。 
  1. 表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
  2. 如果该方法是以执行一个共用 / 通用功能为目的,则可以申明成静态方法。
package com.gch.d1static;

public class Student {
    /*
        实例成员变量:无static修饰,属于对象
     */
    private String name;

    /**
        静态成员方法:有static修饰,归属于类,可以被共享访问,用类名或者对象民都可以访问
     */
    public static int getMax(int age1,int age2){
        return age1 > age2 ? age1 : age2;
    }
    
    /**
        实例方法:属于对象的,只能用对象触发访问
     */
    public void study(){
        System.out.println(name + "正在学习!");
    }

    public static void main(String[] args) {
        // 1.类名.静态成员方法
        System.out.println(Student.getMax(10, 3));
        // 注意:同一个类中,访问静态方法,类名可以省略不写
        System.out.println(getMax(10, 30));

     //   study(); // 报错了
        // 2.对象.实例方法
        Student s = new Student();
        s.name = "小明";
        s.study();

        // 3.对象.静态方法(语法可行,但是不推荐)
        System.out.println(s.getMax(10, 23));
    }
}

 4.static访问的注意事项:

  • 静态方法只能访问静态的成员,不可以" 直接 "访问实例成员。
  • 实例方法中可以访问静态成员,也可以访问实例成员。
  • 静态方法中是不可以出现this关键字的
package com.gch.d1static;

public class Test3 {
    public static void main(String[] args) {
        // 目标:理解static访问相关的语法:面试笔试题,或者以后理解程序很重要的知识(拓展)
        // 静态成员是可以被类和对象共享的
        /* static访问注意事项:
           静态方法只能访问静态的成员,不可以"直接"访问实例成员
           实例方法可以访问静态成员,也可以访问实例成员
           静态方法中是不可以出现this关键字的
         */
    }
    /**
        静态成员
     */
    public static int onlineNumber = 10;
    public static void test2(){
        System.out.println("=====test2====");
    }

    /**
        实例成员
     */
    private String name;
    public void run(){
        System.out.println(name + "跑的快!");
    }

    // 1.静态方法只能访问静态成员,不能"直接"访问实例成员
    public static void test(){
        System.out.println(Test3.onlineNumber);
        System.out.println(onlineNumber);
        test();
     //   System.out.println(name); // 直接报错,静态方法不能直接访问实例成员。
       // run(); //直接报错,静态方法不能直接访问实例成员
    }

    // 2.实例方法可以访问静态成员,也可以访问实例成员
    public void go(){
        System.out.println(Test3.onlineNumber);
        System.out.println(onlineNumber);
        test2();
        System.out.println(this); // this可以出现在实例方法中,代表本类对象的地址
        System.out.println(name);
        run();
    }

    // 3.静态方法中不可以出现this关键字
    public static void test3(){
        // this代表本类对象,而静态方法是归属于类的
//        System.out.println(this); // 直接报错,静态方法中不可以出现this关键字
    }
}

二.static应用知识:工具类

工具类是什么?

  • 工具类类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
  • 使用工具类的好处:一是调用方便,二是提高了代码的复用性,提高了开发效率(一次编写,处处可用)

 为什么工具类中的方法不用实例方法做?

  • 实例方法需要创建对象调用。
  • 而静态方法直接用类名即可访问,此时用对象只是为了调用方法,这样只会浪费内存

工具类定义时的其他要求:

  • 由于工具类里面都是静态方法,直接用类名即可访问,因此工具类无需创建对象,建议将工具类的构造器进行私有。
  • 工具类没有必要被其他类继承,因此将工具类用final修饰为最终类。

package com.gch.d2_static_util;

import java.util.Random;

/**
    工具类
    使用工具类的好处:调用方便,提高了代码的复用性
    使用工具类定义的方法只是为了完成一个公用功能,公用功能写成静态方法,调用方便,只需要通过类名来调用
    如果定义成实例方法则需要创建对象,通过对象来调用,此时用对象只是为了调用方法,这样只会浪费内存
    由于工具类里面都是静态方法,直接用类名即可访问,因此工具类无需创建对象,建议将工具类的构造器进行私有
    将工具类的构造器私有,对外就不能创建对象了
    将工具类用final修饰,不可以被其他类继承
 */
public final class Util {
    // 将工具类的构造器私有,使其不能向外创建对象
    /**
        注意:由于工具类无需创建对象,所以将其构造器私有化会显得很专业
     */
    private Util(){
    }

    /**
        静态方法:随机生成n位验证码
     */
    public static String createVerifyCode(int n){
        // 1.定义一个字符串存储可能出现的字符信息
        String datas = "abcdefghijklmnopqrstovwxyzABCDEFGHIJKLMNOPQRSTOVWXYZ0123456789";
        // 2.定义一个循环,循环n次,每次随机一个索引,提取索引位置出的字符连接起来即可
        // 3.定义一个字符串变量记录存储的字符
        String code = "";
        Random r = new Random();
        for(int i = 0;i< n;i++){
            // 4.随机一个索引
         int index = r.nextInt(datas.length());
         // 5.提取索引对应位置处的字符连接
            code += datas.charAt(index);
        }
        // 6.返回生成的随机验证码
        return code;
    }
}
package com.gch.d2_static_util;

public class Check {
    public static void main(String[] args) {
        // 开发一个验证码  直接通过类名来调用工具类里面的静态方法
        System.out.println(Util.createVerifyCode(4));
//        Util u = new Util(); 直接报错,Util工具类不能对外创建对象
    }
}
package com.gch.d2_static_util;

public class Login {
    public static void main(String[] args) {
        // 开发一个验证码   直接通过类名来调用工具类里面的静态方法
        System.out.println(Util.createVerifyCode(5));
    }
}

package com.gch.d2_static_util;

/**
    定义数组工具类
 */
public class ArrayUtils {
    /**
       构造器私有
     */
    private ArrayUtils(){
    }

    /**
     * 定义静态方法返回整型数组的内容
     * @param arr:传进来的整型数组
     * @return:返回传进来数组的内容
     */
    public static String toString(int[] arr){
        // 一些校验
        if(arr == null){
            return null;
        }
        String result = "[";
        for(int i = 0;i < arr.length;i++){
            result += i == arr.length - 1 ? arr[i] : arr[i] + ", ";
        }
        result += "]";
        return result;
    }

    /**
     * 定义静态方法返回浮点型数组的平均值
     * @param arr:传进来的数组
     * @return:返回浮点型数组去掉最高分和最低分后的平均值
     */
    public static double getAerage(double[] arr){
        // 1.定义三个变量记录当前数组的最大值,最小值,以及总分
        double arrMax = arr[0];
        double arrMin = arr[0];
        double sum = 0;
        for(int i = 0;i < arr.length;i++){
            if(arr[i] > arrMax){
                arrMax = arr[i];
            }
            if(arr[i] < arrMin){
                arrMin = arr[i];
            }
            sum += arr[i];
        }
        System.out.println("当前最高分是:" + arrMax);
        System.out.println("当前最低分是:" + arrMin);
        // 计算平均分
        double average = (sum - arrMax - arrMin) / (arr.length - 2);
        return average;
    }
}
package com.gch.d2_static_util;

/**
    定义测试类,调用该工具类的工具方法,并返回结果
 */
public class TestDemo {
    public static void main(String[] args) {
        // 静态初始化数组
        // 1.静态初始化一个整型数组
        int[] arr = {10,20,30,40};
        int[] arr2 = null;
        int[] arr3 = {};
        System.out.println(ArrayUtils.toString(arr));  // [10, 20, 30, 40]
        System.out.println(ArrayUtils.toString(arr2)); // null
        System.out.println(ArrayUtils.toString(arr3)); // []
        // 2.静态初始化一个浮点型数组
        double[] arr4 = {16.8,29.3,17.6,15.4};
        System.out.println("平均分是:" + ArrayUtils.getAerage(arr4));
                                                        //        当前最高分是:29.3
                                                        //        当前最低分是:15.4
                                                        //        平均分是:17.200000000000006

    }
}

三.static应用知识:代码块

静态代码块的作用是什么?

  • 如果要在启动系统时对静态资源进行初始化,则建议使用静态代码块完成数据的初始化操作。

package com.gch.d3_static_code;

import java.util.ArrayList;

// 先加载类,然后才把mian方法提取到栈内存里面去运行
public class StaticDemo1 {
    // 静态成员:与类一起加载,只在内存中存储1份
    public static String schoolName;
    public static ArrayList<String> cards = new ArrayList<>();
    /**
        静态代码块:由static修饰,属于类,与类一起优先加载一次,自动触发只执行一次
        作用:可以用于初始化静态资源。
     */
    static{
        System.out.println("-------静态代码块被触发执行了!------");
        schoolName = "西安电子科技大学";
        cards.add("3");
    }
    
    public static void main(String[] args) {
        // 目标:先理解静态代码块
        System.out.println("-----main方法执行!");
        System.out.println(schoolName);
    }
}

package com.gch.d3_static_code;

public class StaticDemo2 {
    // 实例成员
    private String name;
    // 无参构造器
    public StaticDemo2(){
        System.out.println("----无参构造器被触发执行----");
    }
    /**
        构造代码块/实例代码块:无static修饰,属于对象,每次创建对象时,都会触发执行一次,并且在构造器执行前执行
        构造代码块/实例代码块:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行
        构造代码块/实例代码块:不属于类,不会与类一起加载
        构造代码块/实力代码块的作用:初始化实例资源
     */
    {
       // name = "张三";
        System.out.println("----实例代码块被触发执行----");
    }

    public static void main(String[] args) {
        // 目标:理解构造代码块(实例代码块),了解,见得少
        StaticDemo2 s1 = new StaticDemo2();
        System.out.println(s1.name);

        StaticDemo2 s2 = new StaticDemo2();
        System.out.println(s2.name);

    }
}

 

package com.gch.d3_static_code;

import java.util.ArrayList;

public class StaticTest3 {
    /**
        1.定义一个静态的集合,这样这个集合只加载一次,因为当前房间只需要一副牌
     */
    public static ArrayList<String> cards = new ArrayList<>();

    /**
        2.在程序真正执行main方法前,把54张牌放进去,后续游戏可以直接使用了
        静态代码块:初始化静态资源
     */
    static{
        // 3.正式做牌,放到集合里面去
        // a.定义一个数组存储全部点数:类型确定了,个数确定了
        // 静态初始化数组
        String[] sizes = {"3","4","5","6","7","8","10","J","Q","K","A","2"};
        // b.定义一个数组存储全部的花色,类型确定了,个数确定了
        String[] colors = {"","","",""};
        // c.遍历点数
        for(int i = 0;i < sizes.length;i++){
            // sizes[i]
            // d.遍历花色
            for(int j = 0;j < colors.length;j++){
                // colors[j]
                // 一张牌
                String card = sizes[i] + colors[j];
                cards.add(card);
            }
        }
        // e.单独加入大小王
        cards.add("大");
        cards.add("小");
    }
    public static void main(String[] args) {
        // 目标:模拟游戏启动前,初始化54张牌数据
        System.out.println("新牌:" + cards);
    }
}

 四.static应用知识:单例设计模式

 单例解决保证一个类对外只产生一个对象

 单例的实现方式很多

  • 饿汉单例模式:饿汉单例模式是在获取对象前,对象已经准备好了,所以饿汉单例模式天生就是线程安全的!
  • 懒汉单例模式:一开始不去创建对象,等到要用这个实例对象的时候,才判断它存不存在真正的实例对象,如果存在我们就返回,不存在才去创建一个对象,那么这样,就可能会有问题,在多个线程都判断它都没有实例对象的时候,可能多个线程会多次创建这个实例对象!
  • ....
  • ....

package com.gch.d4_static_singleinstance;

/**
 * 使用饿汉单例实现单例类
 */
public class SingleInstance {
    /**
     * 饿汉单例是在获取对象前,对象已经提前准备好了一个
     * 2.这个对象只能是一个,所以定义静态成员变量记住,因为静态成员变量在内存中只加载一次
     */
    public static final SingleInstance INSTANCE = new SingleInstance();

    /**
     * 1.必须把构造器私有化
     */
    private SingleInstance() {
        // 防止通过反射创建新的实例
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建~!");
        }
    }
}
package com.gch.d4_static_singleinstance;

public class Test1 {
    public static void main(String[] args) {
       // 目标:理解饿汉单例的设计步骤
        SingleInstance s1 = SingleInstance.instance;
        SingleInstance s2 = SingleInstance.instance;
        System.out.println(s1 == s2); // true,表明s1、s2是同一个对象
    }
}

package com.gch.d4_static_singleinstance;

/**
   懒汉单例
 */
public class SingleInstance2 {
    /**
        1.私有化构造器
     */
    private SingleInstance2() {
    }

    /**
        2.定义一个静态的成员变量负责存储一个对象。
        静态成员变量在内存中只加载一次,在内存中只存储一份
        注意:最好私有化,这样避免给别人挖坑
        定义静态成员变量暂时不存对象
     */
    private static SingleInstance2 instance; // null

    /**
        3.提供一个方法,对外返回单例对象
     */
    public static SingleInstance2 getInstance(){
        if(instance == null){
            // 第一次拿对象:此时需要创建对象
            instance = new SingleInstance2();
        }
        return instance;
    }
}
package com.gch.d4_static_singleinstance;

public class Test2 {
    public static void main(String[] args) {
        // 目标:掌握懒汉单例的设计,理解其思想
        SingleInstance2 s1 = SingleInstance2.getInstance();
        SingleInstance2 s2 = SingleInstance2.getInstance();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1 == s2); // true
    }
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_53622554/article/details/128968102

智能推荐

51单片机的中断系统_51单片机中断篇-程序员宅基地

文章浏览阅读3.3k次,点赞7次,收藏39次。CPU 执行现行程序的过程中,出现某些急需处理的异常情况或特殊请求,CPU暂时中止现行程序,而转去对异常情况或特殊请求进行处理,处理完毕后再返回现行程序断点处,继续执行原程序。void 函数名(void) interrupt n using m {中断函数内容 //尽量精简 }编译器会把该函数转化为中断函数,表示中断源编号为n,中断源对应一个中断入口地址,而中断入口地址的内容为跳转指令,转入本函数。using m用于指定本函数内部使用的工作寄存器组,m取值为0~3。该修饰符可省略,由编译器自动分配。_51单片机中断篇

oracle项目经验求职,网络工程师简历中的项目经验怎么写-程序员宅基地

文章浏览阅读396次。项目经验(案例一)项目时间:2009-10 - 2009-12项目名称:中驰别克信息化管理整改完善项目描述:项目介绍一,建立中驰别克硬件档案(PC,服务器,网络设备,办公设备等)二,建立中驰别克软件档案(每台PC安装的软件,财务,HR,OA,专用系统等)三,能过建立的档案对中驰别克信息化办公环境优化(合理使用ADSL宽带资源,对域进行调整,对文件服务器进行优化,对共享打印机进行调整)四,优化完成后..._网络工程师项目经历

LVS四层负载均衡集群-程序员宅基地

文章浏览阅读1k次,点赞31次,收藏30次。LVS:Linux Virtual Server,负载调度器,内核集成, 阿里的四层SLB(Server Load Balance)是基于LVS+keepalived实现。NATTUNDR优点端口转换WAN性能最好缺点性能瓶颈服务器支持隧道模式不支持跨网段真实服务器要求anyTunneling支持网络private(私网)LAN/WAN(私网/公网)LAN(私网)真实服务器数量High (100)High (100)真实服务器网关lvs内网地址。

「技术综述」一文道尽传统图像降噪方法_噪声很大的图片可以降噪吗-程序员宅基地

文章浏览阅读899次。https://www.toutiao.com/a6713171323893318151/作者 | 黄小邪/言有三编辑 | 黄小邪/言有三图像预处理算法的好坏直接关系到后续图像处理的效果,如图像分割、目标识别、边缘提取等,为了获取高质量的数字图像,很多时候都需要对图像进行降噪处理,尽可能的保持原始信息完整性(即主要特征)的同时,又能够去除信号中无用的信息。并且,降噪还引出了一..._噪声很大的图片可以降噪吗

Effective Java 【对于所有对象都通用的方法】第13条 谨慎地覆盖clone_为继承设计类有两种选择,但无论选择其中的-程序员宅基地

文章浏览阅读152次。目录谨慎地覆盖cloneCloneable接口并没有包含任何方法,那么它到底有什么作用呢?Object类中的clone()方法如何重写好一个clone()方法1.对于数组类型我可以采用clone()方法的递归2.如果对象是非数组,建议提供拷贝构造器(copy constructor)或者拷贝工厂(copy factory)3.如果为线程安全的类重写clone()方法4.如果为需要被继承的类重写clone()方法总结谨慎地覆盖cloneCloneable接口地目的是作为对象的一个mixin接口(详见第20_为继承设计类有两种选择,但无论选择其中的

毕业设计 基于协同过滤的电影推荐系统-程序员宅基地

文章浏览阅读958次,点赞21次,收藏24次。今天学长向大家分享一个毕业设计项目基于协同过滤的电影推荐系统项目运行效果:项目获取:https://gitee.com/assistant-a/project-sharing21世纪是信息化时代,随着信息技术和网络技术的发展,信息化已经渗透到人们日常生活的各个方面,人们可以随时随地浏览到海量信息,但是这些大量信息千差万别,需要费事费力的筛选、甄别自己喜欢或者感兴趣的数据。对网络电影服务来说,需要用到优秀的协同过滤推荐功能去辅助整个系统。系统基于Python技术,使用UML建模,采用Django框架组合进行设

随便推点

你想要的10G SFP+光模块大全都在这里-程序员宅基地

文章浏览阅读614次。10G SFP+光模块被广泛应用于10G以太网中,在下一代移动网络、固定接入网、城域网、以及数据中心等领域非常常见。下面易天光通信(ETU-LINK)就为大家一一盘点下10G SFP+光模块都有哪些吧。一、10G SFP+双纤光模块10G SFP+双纤光模块是一种常规的光模块,有两个LC光纤接口,传输距离最远可达100公里,常用的10G SFP+双纤光模块有10G SFP+ SR、10G SFP+ LR,其中10G SFP+ SR的传输距离为300米,10G SFP+ LR的传输距离为10公里。_10g sfp+

计算机毕业设计Node.js+Vue基于Web美食网站设计(程序+源码+LW+部署)_基于vue美食网站源码-程序员宅基地

文章浏览阅读239次。该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流项目运行环境配置:项目技术:Express框架 + Node.js+ Vue 等等组成,B/S模式 +Vscode管理+前后端分离等等。环境需要1.运行环境:最好是Nodejs最新版,我们在这个版本上开发的。其他版本理论上也可以。2.开发环境:Vscode或HbuilderX都可以。推荐HbuilderX;3.mysql环境:建议是用5.7版本均可4.硬件环境:windows 7/8/10 1G内存以上;_基于vue美食网站源码

oldwain随便写@hexun-程序员宅基地

文章浏览阅读62次。oldwain随便写@hexun链接:http://oldwain.blog.hexun.com/ ...

渗透测试-SQL注入-SQLMap工具_sqlmap拖库-程序员宅基地

文章浏览阅读843次,点赞16次,收藏22次。用这个工具扫描其它网站时,要注意法律问题,同时也比较慢,所以我们以之前写的登录页面为例子扫描。_sqlmap拖库

origin三图合一_神教程:Origin也能玩转图片拼接组合排版-程序员宅基地

文章浏览阅读1.5w次,点赞5次,收藏38次。Origin也能玩转图片的拼接组合排版谭编(华南师范大学学报编辑部,广州 510631)通常,我们利用Origin软件能非常快捷地绘制出一张单独的绘图。但是,我们在论文的撰写过程中,经常需要将多种科学实验图片(电镜图、示意图、曲线图等)组合在一张图片中。大多数人都是采用PPT、Adobe Illustrator、CorelDraw等软件对多种不同类型的图进行拼接的。那么,利用Origin软件能否实..._origin怎么把三个图做到一张图上

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)_电风扇模拟控制系统设计-程序员宅基地

文章浏览阅读4.2k次,点赞4次,收藏51次。51单片机智能电风扇控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0042。_电风扇模拟控制系统设计