📓
Be a Javaer
  • Introduction
  • 第 1 章 Java编程开发入门
    • 第 1 节 Java开发准备
    • 第 2 节 Java基本概念
    • 第 3 节 Java数据类型划分
    • 第 4 节 Java运算符
    • 第 5 节 Java程序逻辑控制
    • 第 6 节 Java方法的定义及使用
  • 第 2 章 Java面向对象编程
    • 第 1 节 类与对象
    • 第 2 节 深入分析类与对象
    • 第 3 节 数组的定义与使用
    • 第 4 节 String类的基本概念
    • 第 5 节 String类的常用方法
    • 第 6 节 this关键字
    • 第 7 节 引用传递
    • 第 8 节 数据表与简单Java类映射
    • 第 9 节 对象比较
    • 第 10 节 static关键字
    • 第 11 节 代码块
    • 第 12 节 内部类
    • 第 13 节 链表的定义与使用
    • 第 14 节 继承性
    • 第 15 节 覆写
    • 第 16 节 数组操作
    • 第 17 节 辅助概念
      • final关键字
      • 多态性
    • 第 18 节 抽象类的定义及使用
    • 第 19 节 接口的定义及使用
    • 第 20 节 Object类
    • 第 21 节 拓展概念
      • 匿名内部类
      • 包装类
    • 第 22 节 包的定义及使用
    • 第 23 节 访问控制权限
      • 单例设计模式
      • 多例设计模式
    • 第 24 节 异常的捕获及处理
    • 第 25 节 Java5新特性
      • 可变参数
      • foreach循环
      • 静态导入
    • 第 26 节 泛型
    • 第 27 节 枚举
    • 第 28 节 Annotation
    • 第 29 节 Java7新特性
      • AutoCloseable
      • Try-with-resources
    • 第 30 节 Java8新特性
      • 接口定义增强
      • Lambda表达式
      • 方法引用
      • 函数式接口
  • 第 3 章 Java高级编程
    • 第 1 节 Java多线程基础实现
    • 第 2 节 线程常用操作方法
    • 第 3 节 线程的同步与死锁
    • 第 4 节 生产者与消费者
    • 第 5 节 Java基础类库
      • StringBuffer
      • Runtime
      • System
      • finalize
      • Cleaner
      • 对象克隆
    • 第 6 节 数字操作类
      • Math类
      • Random类
      • 大数字操作类
    • 第 7 节 日期处理类
      • Date类
      • 日期格式化
      • Calendar类
    • 第 8 节 比较器
    • 第 9 节 正则表达式
      • 常用的正则标记
      • String类对正则的支持
      • java.util.regex包支持
    • 第 10 节 反射机制
    • 第 11 节 动态代理
    • 第 12 节 反射与Annotation
    • 第 13 节 国际化程序实现
    • 第 14 节 开发支持类库
      • Arrays类
      • UUID类
      • Optional类
      • ThreadLocal类
      • 定时器
      • Base64加密工具
    • 第 15 节 文件操作
    • 第 16 节 字节流与字符流
    • 第 17 节 IO辅助概念
      • 字符编码
      • 内存流
      • 管道流
      • RandomAccessFile
    • 第 18 节 打印流
    • 第 19 节 System类对IO的支持
    • 第 20 节 对象序列化
    • 第 21 节 IO高级应用
      • 缓冲输入流
      • Scanner
    • 第 22 节 网络编程
    • 第 23 节 类集框架
    • 第 24 节 List集合
    • 第 25 节 集合输出
    • 第 26 节 Map集合
    • 第 27 节 Set集合
    • 第 28 节 集合工具类
      • Stack
      • Queue
      • Properties
      • Collections工具类
    • 第 29 节 数据流Stream
    • 第 30 节 JDBC简介
    • 第 31 节 Statement接口
    • 第 32 节 PreparedStatment接口
    • 第 33 节 批处理与事务处理
  • 第 4 章 Oracle数据库基础
    • 第 1 节 Oracle简介
    • 第 2 节 Oracle安装与配置
    • 第 3 节 SQLPlus命令
    • 第 4 节 SQL简介与数据表分析
    • 第 5 节 SQL简单查询
    • 第 6 节 SQL限定查询
    • 第 7 节 查询排序
    • 第 8 节 综合练习:基础查询
    • 第 9 节 单行函数
    • 第 10 节 多表查询
    • 第 11 节 分组统计查询
    • 第 12 节 子查询
    • 第 13 节 综合案例:复杂查询
    • 第 14 节 数据更新操作
    • 第 15 节 事务处理
    • 第 16 节 数据伪列
    • 第 17 节 数据表的创建与管理
    • 第 18 节 约束的创建与管理
    • 第 19 节 综合案例:数据表操作
    • 第 20 节 序列的定义与使用
  • 第 5 章 JavaWeb基础
  • 第 6 章 走向单体地狱
  • 第 7 章 GitFlow工作流指南
    • 版本控制
    • Git
    • 集中式工作流
    • 功能分支工作流
    • GitFlow 工作流
    • Forking 工作流
    • Pull Requests
  • 第 8 章 微服务入门
    • 第 1 节 微服务简介
    • 第 2 节 Linux
    • 第 3 节 Docker
    • Docker 仓库
    • Ubuntu 安装 Docker
    • Docker 镜像加速器
    • 第 4 节 Docker Compose
    • 第 5 节 GitLab
    • 第 6 节 Nexus
    • 第 7 节 Harbor
  • 第 9 章 再谈微服务
  • 第 10 章 Spring Boot
  • 第 11 章 Spring Cloud Netflix
  • 第 12 章 Apache Dubbo Zookeeper
  • 第 13 章 Spring Cloud Alibaba
  • 第 14 章 Vue
  • 第 15 章 Kubernetes
  • 第 16 章 Spring Security oAuth2
  • 第 17 章 Flutter
  • Redis
    • Redis 入门
    • Redis 的数据类型
    • Redis 事务
    • Jedis
    • Spring Boot 整合 Redis
    • Redis 配置文件
    • Redis 持久化
    • Redis 发布/订阅
    • Redis 主从复制
    • Redis Sentinel
    • Redis 缓存故障
  • Glossary
由 GitBook 提供支持
在本页
  • static定义属性
  • static定义方法
  • 主方法
  • static的实际应用
  • 总结

这有帮助吗?

  1. 第 2 章 Java面向对象编程

第 10 节 static关键字


static在使用上可以声明属性、方法。

static定义属性

在讲解具体的static作用之前,首先观察一个代码。

范例: 定义程序

class Book { // 描述的是同一个出版社的图书信息
    private String title;
    private double price;
    // 定义一个描述出版社的信息,考虑到方便操作暂不封装
    String pub = "清华大学出版社";
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }
    public String getInfo() {
        return "title=" + title + ", price=" + price + ", pub=" + pub;
    }
}
public class MainClass {
    public static void main(String[] args) {
        Book ba = new Book("Java", 10.2);
        Book bb = new Book("Android", 11.2);
        Book bc = new Book("Oracle", 12.2);
        // 修改了一个属性的内容
        ba.pub = "北京大学出版社";
        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

通过内存关系可以发现此时代码的问题:既然这个类所有图书的出版社都是同一个,那么就没有必要让每个对象都各自占有重复的属性信息。

既然所有对象的pub内容应该是一样的,那么应该将其定义为一个共享的属性,即:所有对象将共享同一个pub属性,那么在这种情况下为了描述出共享的概念,可以使用static来定义属性。

范例: 使用static定义属性

class Book { // 描述的是同一个出版社的图书信息
    private String title;
    private double price;
    // 定义一个描述出版社的信息,考虑到方便操作暂不封装
    static String pub = "清华大学出版社";
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }
    public String getInfo() {
        return "title=" + title + ", price=" + price + ", pub=" + pub;
    }
}
public class MainClass {
    public static void main(String[] args) {
        Book ba = new Book("Java", 10.2);
        Book bb = new Book("Android", 11.2);
        Book bc = new Book("Oracle", 12.2);
        // 修改了一个属性的内容
        ba.pub = "北京大学出版社";
        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

一旦在属性定义上使用了static之后,只要有一个对象修改了属性的内容,那么所有的对象的static属性内容都将一起进行修改,也就是说此时的pub属性就变为了一个公共属性。

static声明的属性与普通属性(非static属性)最大的区别在于保存的内容区域的不同。

既然static是一个公共属性的概念,那么如果只是简单的由一个对象去修改static属性的做法很不合适,那么最好的做法是由所有对象的公共的代表来进行访问,就是类,所以利用static定义的属性是可以由类名称直接调用的。

Book.pub = "北京大学出版社";

此时Book类定义了pub这个static属性,所以可以利用“Book”类直接调用static的pub属性。

static属性与非static属性还有一个最大的区别,所有的非static属性必须产生实例化对象之后才可以访问,但是static属性不受实例化对象的控制,也就是说,在没有实例化对象产生的情况下,依然可以使用static属性。

public class MainClass {
    public static void main(String[] args) {
        // 在没有实例化对象的情况下直接输出属性内容
        System.out.println(Book.pub);
        Book.pub = "北京大学出版社";
        System.out.println(Book.pub);
        Book ba = new Book("Java", 10.2);
        System.out.println(ba.getInfo());
    }
}

所以可以发现sataic定义属性的一个特征:虽然定义在类结构里面,但是并不受到对象的控制,是独立于类存在的。

**疑问?**什么时候使用static属性?什么时候不使用static属性?

在编写类的过程之中,所选择的首要的修饰符一定不是static(95%),如果需要描述出共享信息的时候再使用static(可以方便集体修改,可以不重复开辟内存空间)。

static定义方法

static定义方法的时候也可以在没有实例化对象的时候利用类名称直接调用。

范例: 使用static定义方法

class Book { // 描述的是同一个出版社的图书信息
    private String title;
    private double price;
    // 定义一个描述出版社的信息
    private static String pub = "清华大学出版社";
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }
    public static void setPub(String p) {
        pub = p;
    }
    public String getInfo() {
        return "title=" + title + ", price=" + price + ", pub=" + pub;
    }
}
public class MainClass {
    public static void main(String[] args) {
        // 在没有对象产生的时候进行调用
        Book.setPub("北京大学出版社");
        Book ba = new Book("Java", 10.2);
        Book bb = new Book("Android", 11.2);
        Book bc = new Book("Oracle", 12.2);
        System.out.println(ba.getInfo());
        System.out.println(bb.getInfo());
        System.out.println(bc.getInfo());
    }
}

发现static定义的属性和方法都不受到实例化对象的控制,也就是说属于独立的类的功能。

但是这个时候就会出现一个特别麻烦的问题:此时类中的方法就变成了两组:static方法、非static方法。两组方法间的访问也将受到限制:

  • static方法不能够直接访问非static属性或者是方法,只能够调用static属性或方法;

  • 非static方法可以访问static的属性或者是方法,不受任何的限制。

**分析:**为什么会存在以上的限制?

  • 所有的非static定义的结构,必须在类已经明确的产生了实例化对象才会分配堆空间,才可以使用;

  • 所有的static定义的结构,不受实例化对象的控制,即:可以在没有实例化对象的时候访问。

解决问题:

在最早讲解方法的时候曾经讲过: 如果一个方法定义在了主类方法之中,并且由主方法直接进行调用的话,方法语法:

public static 返回值类型 方法名称(参数类型 参数, ...) {
    [return [返回值];]
}

而后来到了编写类的时候,发现方法的定义格式改变(方法由对象调用)。

public 返回值类型 方法名称(参数类型 参数, ...) {
    [return [返回值];]
}

范例: 观察如下代码

public class MainClass {
    public static void main(String[] args) {
        fun();
    }
    public static void fun() {
        System.out.println("Hello World!");
    }
}

如果此时fun()方法上取消了static,那么就变为了一个非static方法。所有的非static方法必须由对象调用,也就是说此时static方法如果要想使用非static操作,必须产生对象后才可以调用。

public class MainClass {
    public static void main(String[] args) {
        new MainClass().fun();
    }
    public void fun() {
        System.out.println("Hello World!");
    }
}

与属性定义规则一样,定义一个类的时候首先考虑的依然是非static方法,因为所有的类如果保存的信息多(有属性),那么每一个对象执行同一个方法的时候,就可以利用自己的属性实现方法的不同调用。

class Book {
    private boolean flag;
    public Book(boolean flag) {
        this.flag = flag;
    }
    public void fun() {
        if (flag) {
            System.out.println("可以操作!");
        } else {
            System.out.println("不可以操作哦!");
        }
    }
}
public class MainClass {
    public static void main(String[] args) {
        Book ba = new Book(true);
        Book bb = new Book(false);
        ba.fun();
        bb.fun();
    }
}

但如果说现在一个类里面没有任何的属性存在,只有方法,建议可以将所有的方法定义为static方法,这样就不用在每次调用的时候实例化对象。

范例: 定义一个数学的加法操作

class MyMath {
    public static int add(int x, int y) {
        return x + y;
    }
}
public class MainClass {
    public static void main(String[] args) {
        System.out.println(MyMath.add(10, 20));
    }
}

此时MyMath类没有属性,产生对象完全没有任何的意义,所以会使用static方法。

写一个类的时候优先考虑的一定是非static方法或非static变量(95%)。

主方法

下面来观察Java main方法的组成:public static void main(String[] args)

  • public:主方法是任何程序的开始,所以这个方法对任何的操作都一定是可见的,就必须使用public;

  • static:证明此方法是由类名称调用的;

  • void:主方法是一切执行的起点;

  • main:Java规定的一个方法名称,不能修改;

  • String[] args:程序运行时传递的参数;

范例: 得到参数

public class MainClass {
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i ++) {
            System.out.println(args[i]);
        }
    }
}

所有输入的参数必须使用空格分割,例如:“java MainClass 参数 参数 参数”。

如果想要在参数中输入空格,则可以使用“"”描述:“java MainClass "参数 参数"”。

static的实际应用

在之前得到的结论:

  • 不管有多少个对象,都使用同一个static属性;

  • 使用static方法可以避免实例化对象调用方法的限制。

**功能一:**实现类实例化对象个数的统计

希望每当实例化一个类对象的时候都可以打印一个信息:产生的第x个实例化对象。

因为只要是新的实例化对象产生了,那么就一定会去调用类中的构造方法,所以可以在构造方法里面增加一个统计数据的操作,每当新对象产生之后统计的内容就自增一个。

class Book {
    private static int num = 0;
    public Book() {
        num ++;
        System.out.println("这是第" + num + "产生的对象!");
    }
}
public class MainClass {
    public static void main(String[] args) {
        new Book();
        new Book();
        new Book();
        new Book();
        new Book();
        new Book();
    }
}

**功能二:**实现属性的自动设置

例如,现在某一个类有一个无参构造方法,一个有参构造方法,有参构造方法主要的目的是传递一个title属性,但是希望不管调用的是无参的还是有参的构造,都可以为title设置内容。

class Book {
    private String title;
    private static int num = 0;
    public Book() {
        this("NOTITLE - " + num ++);
    }
    public Book(String title) {
        this.title = title;
    }
    public String getTitle() {
        return this.title;
    }
}
public class MainClass {
    public static void main(String[] args) {
        System.out.println(new Book("Java").getTitle());
        System.out.println(new Book().getTitle());
        System.out.println(new Book().getTitle());
    }
}

此时就算是没有设置title属性的内容,那么最终的属性也不会是null。

总结

1、开发之中首选的属性一定不是static属性,首选的方法一定不是static方法;

2、static属性和方法可以在没有实例化对象的时候直接由类名称进行调用;

3、static属性保存在全局数据区

内存区一共有四个:栈内存、堆内存、全局数据区、全局代码区。

上一页第 9 节 对象比较下一页第 11 节 代码块

最后更新于5年前

这有帮助吗?