百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

JAVA笔记(十四)面向对象——关键字static

yuyutoo 2024-10-12 00:55 7 浏览 0 评论

static:

在java的关键字中,static和final是两个我们必须掌握的关键字。不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构。下面我们来具体讲解static的使用。

1.用途:

static关键字有两种作用:

  • 第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关
  • 第二,实现某个方法或属性与类而不是对象关联在一起

具体而言,在Java语言中,static主要有4种使用情况:成员变量、成员方法、代码块和静态导包

1).static修饰成员变量:

Java类提供了两种类型的变量:用static关键字修饰的静态变量和不用static关键字修饰的实例变量。静态变量属于类,在内存中只有一个复制,只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使用了。对静态变量的引用有两种方式,分别是“类.静态变量"和”对象.静态变量";

//静态变量使用方式一:
Student.id;                     // 类.静态变量;Student为类,id为静态属性
//静态变量使用方式二:
Student student=new Student;
student.id;                     //对象.静态变量;student为学生对象,id为静态属性

实例变量属于对象,只有对象被创建后,实例变量才会被分配内存空间,才能被使用,它在内存中存在多个复制,只有用“对象.实例变量”的方式来引用。

Student student=new Student();
student.id;           //对象.实例变量    student为对象,id为非静态变量

我们同样使用案例来具体讲解static修饰属性的使用:


public class Person {
    String name;
    int age; 
    public String toString() {
        return "Name:" + name + ", Age:" + age;
    }
   
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "zs";
        p1.age = 10;
        Person p2 = new Person();
        p2.name = "ls";
        p2.age = 12;
        System.out.println(p1);
        System.out.println(p2);
    }

}

打印输出结果为:



上面的代码我们很熟悉,根据Person构造出的每一个对象都是独立存在的,保存有自己独立的成员变量,相互不会影响,他们在内存中的示意如下:



从上图中可以看出,p1和p2两个变量引用的对象分别存储在内存中堆区域的不同地址中,所以他们之间相互不会干扰。但其实,在这当中,我们省略了一些重要信息,相信大家也都会想到,对象的成员属性都在这了,由每个对象自己保存,那么他们的方法呢?实际上,不论一个类创建了几个对象,他们的方法都是一样的:



从上面的图中我们可以看到,两个Person对象的方法实际上只是指向了同一个方法定义。这个方法定义是位于内存中的一块不变区域(由jvm划分),我们暂称它为静态存储区。这一块存储区不仅存放了方法的定义,实际上从更大的角度而言,它存放的是各种类的定义,当我们通过new来生成对象时,会根据这里定义的类的定义去创建对象。多个对象仅会对应同一个方法,这里有一个让我们充分信服的理由,那就是不管多少的对象,他们的方法总是相同的,尽管最后的输出会有所不同,但是方法总是会按照我们预想的结果去操作,即不同的对象去调用同一个方法,结果会不尽相同。

我们知道,static关键字可以修饰成员变量和方法,来让它们变成类的所属,而不是对象的所属,比如我们将Person的age属性用static进行修饰,结果会是什么样呢?请看下面的例子:

public class Person {
    String name;
    static int age;     //name属性修饰为static
    
    /* 其余代码不变... */
}

打印输出结果为:



我们发现,结果发生了一点变化,在给p2的age属性赋值时,干扰了p1的age属性,这是为什么呢?我们还是来看他们在内存中的示意:




我们发现,给age属性加了static关键字之后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象如果对age属性做了改变,其他的对象都会受到影响。我们看到此时的age和toString()方法一样,都是交由类去管理。

虽然我们看到static可以让对象共享属性,但是实际中我们很少这么用,也不推荐这么使用。因为这样会让该属性变得难以控制,因为它在任何地方都有可能被改变。如果我们想共享属性,一般我们会采用其他的办法:

package cn.hz;

/**
 * @author hz
 * @version 1.0
 */
public class Person {
    private static int count = 0;     //编号依次递增,共享数据
    int id;
    String name;
    int age;

    public Person() {
        id = ++count;
    }

    public String toString() {
        return "Id:" + id + ", Name:" + name + ", Age:" + age;
    }

    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "zs";
        p1.age = 10;
        Person p2 = new Person();
        p2.name = "ls";
        p2.age = 12;
        System.out.println(p1);
        System.out.println(p2);
    }
}

打印输出结果为:



id为编号依次递增,需要进行资源共享,其他属性为各自属性,互不影响,上面的代码起到了给Person的对象创建一个唯一id以及记录总数的作用,其中count由static修饰,是Person类的成员属性,每次创建一个Person对象,就会使该属性自加1然后赋给对象的id属性,这样,count属性记录了创建Person对象的总数,由于count使用了private修饰,所以从类外面无法随意改变。

2).static修饰成员方法:

Java中提供了static方法和非static方法。static方法是类的方法,不需要创建对象就可以被调用,而非static方法是对象的方法,只有对象被创建出来后才可以被使用

static方法中不能使用this和super关键字,不能调用非static方法,只能访问所属类的静态成员变量和成员方法,因为当static方法被调用时,这个类的对象可能还没被创建,即使已经被创建了,也无法确定调用哪个对象的方法。同理,static方法也不能访问非static类型的变量。

package cn.hz;

/**
 * @author hz
 * @version 1.0
 静态方法只能调用静态属性和静态方法,不能调用非静态属性和非静态方法
 */
public class Person {
    public static  String name;   //静态属性
    public static void show(){
        System.out.println("静态方法");
    }
    public static void print(){
        System.out.println("调用静态属性:"+name);           //调用静态属性
        show();                                               //调用静态方法
    }
}

在使用静态方法时注意静态方法只能调用静态属性和静态方法,不能调用非静态属性和非静态方法。

静态方法的使用场合,单例模式:

package cn.hz;

/**
 * @author hz
 * @version 1.0
 * 单例模式:懒汉模式
 */
public class Singleton {
    //定义私有静态属性
    private static Singleton instance=null;
    //构造方法私有化
    private Singleton(){}
    //提供公共的访问方式,因为构造方法私有化,所有不允许在外部创建对象,只能通过类进行调用该方法
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

这是我们java中设计模式中的一种单例模式中的懒汉模式,该模式会将构造方法私有化,即不允许外部创建对象,那么如何将类中创建好的对象提供给外部使用呢?将外部访问方式定义为static静态方法,直接通过类.静态方法进行调用。

3).静态代码块

static代码块在类中是独立于成员变量和成员函数的代码块的。注意: 这些static代码块只会被执行一次,在看静态代码块之前我们先来看一个例子:

package cn.hz;

/**
 * @author hz
 * @version 1.0
 * 定义一个Book类
 */
public class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}
package cn.hz;

/**
 * @author hz
 * @version 1.0
 *
 * 定义一个Person类:Book对象作为属性
 */
public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");

    public Person(String msg) {
        System.out.println(msg);
    }

    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");

    public static void main(String[] args) {
        Person p1 = new Person("p1初始化");
    }
}

打印输出结果为:


上面的例子中,Person类中组合了四个Book成员变量,两个是普通成员,两个是static修饰的类成员。我们可以看到,当我们new一个Person对象时,static修饰的成员变量首先被初始化,随后是普通成员,最后调用Person类的构造方法完成初始化。也就是说,在创建对象时,static修饰的成员会首先被初始化,而且我们还可以看到,如果有多个static修饰的成员,那么会按照他们的先后位置进行初始化。

实际上,static修饰的成员的初始化可以更早地进行,请看下面的例子:

package cn.hz;

/**
 * @author hz
 * @version 1.0
 * 定义一个Book类
 */
public class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}
package cn.hz;

/**
 * @author hz
 * @version 1.0
 *
 * 定义一个Person类:Book对象作为属性
 */
public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");

    public Person(String msg) {
        System.out.println(msg);
    }

    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");

    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }

    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
    }
}

打印输出结果为:



在上面的例子中我们可以发现两个有意思的地方,第一个是当我们没有创建对象,而是通过类去调用类方法时,尽管该方法没有使用到任何的类成员,类成员还是在方法调用之前就初始化了,这说明,当我们第一次去使用一个类时,就会触发该类的成员初始化。第二个是当我们使用了类方法,完成类的成员的初始化后,再new该类的对象时,static修饰的类成员没有再次初始化,这说明,static修饰的类成员,在程序运行过程中,只需要初始化一次即可,不会进行多次的初始化。

回顾了对象的初始化以后,我们再来看static的第三个作用就非常简单了,那就是当我们初始化static修饰的成员时,可以将他们统一放在一个以static开始,用花括号包裹起来的块状语句中:

package cn.hz;

/**
 * @author hz
 * @version 1.0
 * 定义一个Book类
 */
public class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}
package cn.hz;

/**
 * @author hz
 * @version 1.0
 *
 * 定义一个Person类:Book对象作为属性
 */
public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2;

    static {
        book2 = new Book("static成员book2成员变量初始化");
        book4 = new Book("static成员book4成员变量初始化");
    }

    public Person(String msg) {
        System.out.println(msg);
    }

    Book book3 = new Book("book3成员变量初始化");
    static Book book4;

    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }

    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("****************");
        Person p1 = new Person("p1初始化");
    }
}

打印输出结果为:



通过结果我们发现两者结果相同,但是在代码上我们进行了相应的优化,静态块在实际使用场合还是比较多,一般用于资源的初始化,比如后期我们在JDBC加载数据库连接信息时我们就可以使用静态块进行初始化。

4).静态导包:

相比于上面的三种用途,第四种用途可能了解的人就比较少了,但是实际上它很简单,而且在调用类方法时会更方便。以上面的“PrintHelper”的例子为例,做一下稍微的变化,即可使用静态导包带给我们的方便:

package cn.hz;
public class PrintHelper {
    public static void print(Object o){
        System.out.println(o);
    }
}
import static cn.hz.PrintHelper.*;

public class App 
{
    public static void main( String[] args )
    {
        print("Hello World!");
    }
}

打印输出结果为:Hello Word!

上面的代码来自于两个java文件,其中的PrintHelper很简单,包含了一个用于打印的static方法。而在App.java文件中,我们首先将PrintHelper类导入,这里在导入时,我们使用了static关键字,而且在引入类的最后还加上了“.*”,它的作用就是将PrintHelper类中的所有类方法直接导入。不同于非static导入,采用static导入包后,在不与当前类的方法名冲突的情况下,无需使用“类名.方法名”的方法去调用类方法了,直接可以采用"方法名"去调用类方法,就好像是该类自己的方法一样使用即可。

总结:

  • 用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
  • 用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
  • 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
  • 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。

相关推荐

深度解读Spring框架的核心原理

深度解读Spring框架的核心原理在Java开发的世界里,提到Spring框架,就像提起一位久经沙场的老将,它几乎成了企业级应用开发的代名词。那么,这个被无数开发者膜拜的框架究竟有何独特之处?今天,我...

「Spring认证」Spring 框架概述

Spring是最流行的企业Java应用程序开发框架。全球数以百万计的开发人员使用SpringFramework来创建高性能、易于测试和可重用的代码。Spring框架是一个开源的Java...

学习Spring框架 这一篇就够了

1.spring概述1.1Spring是什么(理解)...

Spring框架双核解析:IOC与AOP的本质与实战

#Spring核心#IOC容器#AOP编程#Java框架设计...

Spring Boot与传统Spring框架的对比:探索Java开发的新境界

SpringBoot与传统Spring框架的对比:探索Java开发的新境界在Java生态系统中,Spring框架无疑是一个里程碑式的存在。从最初的简单依赖注入容器,到如今覆盖企业级开发方方面面的庞大...

Spring MVC框架源码深度剖析:从入门到精通

SpringMVC框架源码深度剖析:从入门到精通SpringMVC框架简介SpringMVC作为Spring框架的一部分,为构建Web应用程序提供了强大且灵活的支持。它遵循MVC(Model-V...

Spring框架入门

一.spring是什么?Spring是分层...

程序员必知必会技能之Spring框架基础——面向切面编程!

面向切面编程AOP(AspectOrientedProgramming)与OOP(ObjectOrientedProgramming,面向对象编程)相辅相成。AOP提供了与OOP不同的抽象软件结...

Spring Security安全框架深度解读:为你的应用穿上“钢铁铠甲”

SpringSecurity安全框架深度解读:为你的应用穿上“钢铁铠甲”在现代网络世界里,保护我们的应用程序免受各种威胁攻击至关重要。而在这个过程中,SpringSecurity框架无疑是我们最可...

Spring框架的设计哲学与实现:打造轻量级的企业级Java应用

Spring框架的设计哲学与实现:打造轻量级的企业级Java应用Spring框架自2003年诞生以来,已成为企业级Java应用开发的代名词。它不仅仅是一个框架,更是一种设计理念和哲学的体现。本文将带你...

Spring框架深度解析:从核心原理到底层实现的全方位避坑指南

一、Spring框架核心概念解析1.控制反转(IoC)与依赖注入(DI)Spring的核心思想是通过IoC容器管理对象的生命周期和依赖关系。传统开发中,对象通过new主动创建依赖对象,导致高耦合;而S...

Java框架 —— Spring简介

简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入...

Spring 框架概述,模块划分

Spring框架以控制反转(InversionofControl,IoC)和面向切面编程(Aspect-OrientedProgramming,AOP)为核心,旨在简化企业级应用开发,使开发者...

spring框架怎么实现依赖注入?

依赖注入的作用就是在使用Spring框架创建对象时,动态的将其所依赖的对象注入到Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入。具体介绍如下:●属性set...

Spring框架详解

  Spring是一种开放源码框架,旨在解决企业应用程序开发的复杂性。一个主要优点就是它的分层体系结构,层次结构让你可以选择要用的组件,同时也为J2EE应用程序开发提供了集成框架。  Spring特征...

取消回复欢迎 发表评论: