java面向对象进阶之static

static静态关键字

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

1.static是静态的意思,可以修饰成员变量和成员方法。
2.static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问、修改。

成员变量可以分为两类:
1.静态成员变量(有static修饰,属于类,内存中加载一次):常表示如在线人数信息,等需要被共享的信息,可以被共享访问。

public class User{
// 静态成员变量
public static String onlineNumber = 160;
}
类名.静态成员变量。
对象.静态成员变量。

User类:

package com.itheima.staticdom;

public class User {
    // 在线人数,静态成员变量,只在内存中存一份
    public static int onlineNumber = 160;
}

StaticFieldDemo.java类:

package com.itheima.staticdom;

public class StaticFieldDemo {
    public static void main(String[] args) {
        // static修饰成员变量的作用和访问特点
        // 类名.静态成员变量。
        System.out.println(User.onlineNumber);
        // 对象访问
        User user = new User();
        User.onlineNumber++;
        System.out.println(user.onlineNumber);
    }
}

2.实例成员变量(无static修饰,存在于每个对象中):常表示姓名name、年龄age、等属于每个对象的信息。

public class User{
	// 实例成员变量
	private String name;
    private int age;
}
对象.实例成员变量。
package com.itheima.staticdom;

public class User {
    // 在线人数,静态成员变量,只在内存中存一份
    public static int onlineNumber = 160;
    // 实例成员变量
    private String name;
    private int age;

    public static void main(String[] args) {
        // 对象名.实例成员变量
        User user = new User();
        user.name = "asnowdream";
        user.age = 21;
        System.out.println(user.name);
        System.out.println(user.age);
        // 类名.静态成员变量
        System.out.println(User.onlineNumber);
        // 用一个类中静态成员变量的访问可以省略类名。
        System.out.println(onlineNumber);
    }
}

两种成员变量各自在什么情况下定义?
1.静态成员变量:表示在线人数等需要被共享的信息。
2.实例成员变量:属于每个对象,且每个对象的该信息不同时,如:name,age,money。

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

1.首先把User.class类加载到方法区,同时在堆内存中开辟一块User类静态变量区加载静态变量,然后把main方法提取到栈里面,接下来执行main方法的第一行代码,直接到堆内存中找到onLineNumber静态成员变量,同样第二行,三行,寻找的方法都相同,然后执行相应的操作。
注意:name、age属于对象成员变量不会开始就在栈内存中加载。

在这里插入图片描述
2.接下来在栈内存中开辟一块名为u1的内存空间存储推内存实例化对象的内存地址,堆内存中的对象里面有成员变量name和age,然后经过对象对变量进行赋值,然后通过对象名访问对象中的实例化对象,然后还可用对象名访问静态成员变量(静态成员变量被所有共享和对象可以建立一种关系,对象可以找到静态成员变量),建议使用类名访问静态成员变量更快,接下来创建了新的对象,开辟了新的内存空间,对象之间实例化对象相互独立存储。
在这里插入图片描述

static修饰成员方法的基本用法

之前学习中定义方法有的有static修饰,有的没有,两者之间有什么不同?

public void run(){
	System.out.print("snowdream正在好好学习,天天想向上!");
}
public static int getMax(int a,int b){
	return a>b?a:b;
}

成员方法的分类:
1.静态成员方法(有static修饰,归属于类),建议用类名访问,也可以用对象访问。
· 类名.静态成员方法
· 对象.静态成员方法(不建议)
2.实例成员方法(无static修饰,归属于对象),只能用对象触发访问。
· 对象.静态成员方法

package com.itheima.staticdom;

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) {
        // 类名.静态成员方法
        System.out.println(Student.getMax(10, 3));
        // 注意:同一个类中,访问静态方法,类名可以省略不写。
        System.out.println(getMax(10, 33));
        // study() 报错了
        // 对象,实例方法
        Student s = new Student();
        s.name = "孙悟空";
        s.study();
        // 对象,静态方法(语法可行,但是不推荐)
        System.out.println(s.getMax(33, 22));
    }
}

使用场景:
1.表示对象自己的行为的,且方法中需要访问实例成员的,则该方法必须申明成实例方法。
2.如果该方法是以执行一个共用功能为目的,则可以申明成静态方法。

static修饰成员方法的内容原理

1.首先把Student.class类和main、getMax静态方法加载到方法区,然后把main方法提取到栈内存中,然后执行main方法中的第一行代码,首先在方法区找到getMax方法然后提取到栈内存执行,然后方法返回结果,然后下一行代码相同访问方式,假如直接调用study方法会报错,因为没有实例化对象找不到该方法,要想调用study方法,首先要在栈内存中开辟一个名为s的内存空间存储堆内存中实例化对象的内存地址,然后通过内存地址访问实例成员变量并赋值,在创建对象时对象中的方法引用指向方法区中加载study实例方法,然后通过对象可以调用study方法。
在这里插入图片描述

static的注意事项

1.静态方法只能访问静态的成员,不可以直接访问实例成员。
2.实例方法可以访问静态的成员,也可以访问实例成员。
3.静态方法中是不可以出现this关键字的。

package com.itheima.staticdom;

public class Test {
    // 静态成员变量
    public static int onLineNumber = 10;
    // 静态成员方法
    public static void test2(){
        System.out.println("==test2==");
    }
    // 实例成员变量
    private String name;
    // 实例成员方法
    public void run(){
        System.out.println(name+"跑的块~~");
    }

    // 静态方法中是不可以出现this关键字的。
    public static void test3(){
        // this只能代表当前对象!! 静态方法可以不用对象调用。
        // System.out.println(this);
    }

    // 实例方法可以访问静态的成员,也可以访问实例成员。
    public void go(){
        // 访问静态成员变量
        System.out.println(Test.onLineNumber);
        System.out.println(onLineNumber);
        // 访问静态方法
        test2();
        // 访问实例成员变量
        System.out.println(name);
        // this可以出现在实例方法中,泛指当前对象的地址
        System.out.println(this);
        // 访问实例成员方法
        run();
    }

    // 静态方法只能访问静态的成员,不可以直接访问实例成员。
    public static void test(){
        // 访问静态成员变量
        System.out.println(Test.onLineNumber);
        System.out.println(onLineNumber);
        // 访问静态方法
        test2();
        // 不能直接访问实例成员变量
        // System.out.println(name);
        // 不能直接访问实例成员方法
        // run();
    }
    public static void main(String[] args) {

    }
}

static应用知识:工具类

工具类是什么?
类中都是 一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用的。
案例:
在企业的管理系统中,通常需要在一个系统的很多业务除使用验证码进行防刷新等安全控制。

问题:同一个功能多处开发,会出现代码重复度过高。
Util.java工具类:

package com.itheima.d2_static_util;
import java.util.Random;

/**
 * 工具类
 */
public class Util {
    /**
     * 注意:由于工具类无需创建对象,所以把其构造器私有化。
     */
    private Util() {
    }

    /**
     * 静态方法 验证码生成模块
     * @param n 验证码位数
     * @return 返回生成的验证码
     */
    public static String createVerifyCode(int n){
        // 开发一个验证码
        // 定义一个变量记住验证码
        String code = "";
        // 定义一个变量记住全部验证码字符
        String data = "ABCDEFGHIJKLMNOPRSTUVWXYZabcdefghijklmnoprstuvwxyz0123456789";
        // 定义一个循环生成几个随机索引,去得到几个字符串
        Random r = new Random();
        for (int i = 0; i < n; i++) {
            // 获取随机索引对应的字符,链接给code
            int index = r.nextInt(data.length());
            code += data.charAt(index);
        }
        return code;
    }
}

longin.java登陆引用类:

package com.itheima.d2_static_util;

public class longin {
    public static void main(String[] args) {
        // 开发一个验证码
        System.out.println("验证码:"+ Util.createVerifyCode(5));
    }
}

Check.java检测引用类:

package com.itheima.d2_static_util;

public class Check {
    public static void main(String[] args) {
        // 开发一个验证码
        System.out.println("验证码:"+ Util.createVerifyCode(5));
    }
}

使用工具类的好处:一是调用方便,二是提高了代码复用(一次编写,处处可用)
为什么工具类中的方法不用实例方法做呢?
1.实例方法需要创建对象调用。
2.此时用对象只是为了调用方法,这样只会浪费内存。

工具类定义时的其他要求:
由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。

package com.itheima.d2_static_util;

/**
 * 工具类
 */
public class Util {
    /**
     * 注意:由于工具类无需创建对象,所以把其构造器私有化。
     */
    private Util() {}
}

eg:定义数组工具类
需求:在实际开发中,经常会遇到一些数组使用的工具类,请按照如下要求编写一个数组的工具类:ArraysUtils。
1.我们知道数组对象直接输出的时候是输出对象的地址的,而项目中很多地方都需要返回数组的内容,请在ArraysUtils中提供一个工具类方法toString,用于返回整数数组的内容,返回的字符串格式如:[10,20,50,34,100](只考虑整数数组,且只考虑一维数组)
2.经常需要统计平均值,平均值为去掉最低分和最高分后的分值,请提供这样一个工具方法getAerage,用于返回平均分。(只考虑浮点型数组,且只考虑一维数组)
3.定义一个测试类TestDemo,调用该工具类的工具方法,并返回结果。

ArraysUtils工具类:

package com.itheima.d2_static_util;

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

    /**
     * 字符串列表
     * @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 float getAerage(int[] arr){
        // 校验
        if(arr == null){
            return 0;
        }
        int max = arr[0];
        int min = arr[0];
        int age = 0;
        for (int i = 0; i < arr.length; i++) {
            if (max <= arr[i]){
                max = arr[i];
            }else if (min >= arr[i]){
                min = arr[i];
            }
            age += arr[i];
        }
        return (float) (age-max-min) / (arr.length-2);
    }
}

TestDemo.java测试类:

package com.itheima.d2_static_util;

public class TestDemo {
    public static void main(String[] args) {
        int[] arr = null;
        int[] arr1 = {};
        int[] arr2 = {12,23,44,99};
        int[] arr3 = {1,2,3,4};

        System.out.println("数组为:"+ArrayUtil.toString(arr));
        System.out.println("数组为:"+ArrayUtil.toString(arr1));
        System.out.println("数组为:"+ArrayUtil.toString(arr2));
        System.out.println("最终成绩为:"+ArrayUtil.getAerage(arr3)+"分。");
    }
}

static应用知识:代码块

代码块的分类、作用

代码块概述:
1.代码块是类的5大成分之一(成员变量、构造器、方法、代码块、内部类),定义在类中方法外。
2.在Java类下,使用{}括起来的代码被称为代码块。

代码块分为:
1.静态代码块:
格式:static{}
特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次。
使用场景:在类加载的时候做一些静态数据初始化的操作,以便后续使用。

2.构造代码块:
格式:{}
特点:每次创建对象,调用构造器执行时,都会执行该代码块中的代码,并且在构造器执行前执行。
使用场景:初始化实例资源。

package com.itheima.d3_static_code;

public class StaticDemo {

    public static void main(String[] args) {
        System.out.println("------------main方法执行了!---------");
    }
    /**
     * 静态代码块,有static修饰,属于类,与类一起优先加载一次,自动触发执行。
     * 作用:可以用于初始化静态资源。
     */
    static {
        System.out.println("------------静态代码块执行了!--------------");
    }
}

在这里插入图片描述

package com.itheima.d3_static_code;

public class StaticDemo2 {
    // 无参构造器
    public StaticDemo2(){
        System.out.println("========无参构造器被触发执行==========");
    }
    /**
     * 实例代码块:无static修饰,属于对象,每次构建对象时,都会触发一次执行。
     * 作用:初始化实例资源
     */
    {
        System.out.println("========实例代码块被触发执行==========");
    }
    public static void main(String[] args) {
        StaticDemo2 s = new StaticDemo2();
        StaticDemo2 s1 = new StaticDemo2();
    }
}

在这里插入图片描述

静态代码块的应用案例

eg:斗地主游戏
在这里插入图片描述
需求:
再启动游戏房间的时候,应该提前准备好54张牌,后续才可以直接使用这些牌数据。
分析:
1.该房间只需要一副牌。
2.定义一个静态的ArrayList集合存储54张牌对象,静态的集合只会加载一份。
3.在启动游戏房间前,应该将54张牌初始化好。
4.当系统启动的同时需要准备好54张牌数据,此时可以用静态代码块完成。

package com.itheima.d3_static_code;
import java.util.ArrayList;

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

    /**
     *在程序真正运行main方法前,把54张牌放进去把,后续游戏可以直接使用。
     */
    static {
        // 正式做牌,放到集合中去,定义一个数组存全部点数,类型确定了,个数确定了。
        String [] sizes = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
        // 定义一个数组存储全部的花色,类型确定了,个数确定了。
        String [] colors = {"♦","♥","♣","♠"};
        // 遍历点数
        for (int i = 0; i < sizes.length; i++) {
            // 遍历花色
            for (int j = 0; j < colors.length; j++) {
                // 一张牌
                String card = sizes[i] + colors[j];
                cards.add(card);
            }
        }
        // 单独加入大小王
        cards.add("大🃏");
        cards.add("小🃏");
    }

    public static void main(String[] args) {
        // 模拟游戏启动前,初始化54张牌数据。
        System.out.println("新牌:"+cards);
    }
}

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

设计模式、单例模式介绍、饿汉单例模式

什么是设计模式?
1.开发中经常遇到一些问题,一个问题通常有n种解法的,但其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
2.设计模式有20多种,对应20多种软件开发中会遇到的问题。
3.学设计模式主要是有2点:
第一:这种模式用来解决什么问题。
第二:遇到这种问题了,该模式是怎么写的,他是如何解决这个问题的。
单例模式:
1.可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。
2.例如任务管理器对象我们只需要一个就可以解决问题了,这样可以节省内存空间。

饿汉单例设计模式:
在用类获取对象的时候,对象已经提前为你创建好了。
设计步骤:
1.定义一个类,把构造器私有。
2.定义一个静态变量存储一个对象。

SingleInstance.java类:

package com.itheima.danli;

/**定义一个饿汉单例类*/
public class SingleInstance {
    /**定义一个静态变量存储一个对象即可:属于类,与类一起加载一次*/
    public static SingleInstance instance = new SingleInstance();
    /**单例必须私有构造器*/
    private SingleInstance(){
        System.out.println("创建了一个对象");
    }
}

Test.java测试类:

package com.itheima.danli;

public class Test {
    public static void main(String[] args) {
        //可以通过类名调用静态变量存储的对象
        SingleInstance s = SingleInstance.instance;
        SingleInstance s1 = SingleInstance.instance;
        System.out.println(s == s1);   // true
    }
}

说明没有创建新对象,始终就是一个对象,因为对象内存地址相同,并且这个对象在调用的时候都实例化成功了。优点就是获取对象速度快,
懒汉单例模式:
在真正需要该对象的时候,才去创建一个对象(延迟加载对象)。
设计步骤:
1.定义一个类,把构造器私有。
2.定义一个静态变量存储一个对象。

Singledanli.java单例类:

package com.itheima.danli;

public class Singledanli {
    // 定义一个静态成员变量负责存储对象。只加载一次,内存中只有一份。最好私有化
    private static Singledanli instance;
    // 提供一个方法对外返回单例对象。
    public static Singledanli getInstance(){
        if(instance == null){
            // 第一次来拿对象,此时需要创建对象。
            instance = new Singledanli();
        }
        return instance;
    }
    // 私有化构造器
    private Singledanli() {
    }
}

Test1.java测试类:

package com.itheima.danli;

public class Test1 {
    public static void main(String[] args) {
        // 获取单例类对象
        Singledanli s = Singledanli.getInstance();
        Singledanli s1 = Singledanli.getInstance();
        System.out.println(s == s1);  // true
    }
}

说明没有创建新对象,始终就是一个对象,因为对象内存地址相同,但是这个对象在调用的时候才开始创建。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值