Java提高篇 —— Java关键字之static的四种用法

一、前言

 

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

 

二、static关键字

 

1、修饰成员变量 

       在我们平时的使用当中,static最常用的功能就是修饰类的属性和方法,让他们成为类的成员属性和方法,我们通常将用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 = "zhangsan";p1.age = 10;Person p2 = new Person();p2.name = "lisi";p2.age = 12;System.out.println(p1);System.out.println(p2);}/**Output* Name:zhangsan, Age:10* Name:lisi, Age:12*///~
}

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

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

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

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

public class Person {String name;static int age;/* 其余代码不变... *//**Output* Name:zhangsan, Age:12* Name:lisi, Age:12*///~
}

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

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

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

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 = "zhangsan";p1.age = 10;Person p2 = new Person();p2.name = "lisi";p2.age = 12;System.out.println(p1);System.out.println(p2);}/**Output* Id:1, Name:zhangsan, Age:10* Id:2, Name:lisi, Age:12*///~
}

       上面的代码起到了给Person的对象创建一个唯一id以及记录总数的作用,其中count由static修饰,是Person类的成员属性,每次创建一个Person对象,就会使该属性自加1然后赋给对象的id属性,这样,count属性记录了创建Person对象的总数,由于count使用了private修饰,所以从类外面无法随意改变。

 

2、修饰成员方法

       static的另一个作用,就是修饰成员方法。相比于修饰成员属性,修饰成员方法对于数据的存储上面并没有多大的变化,因为我们从上面可以看出,方法本来就是存放在类的定义当中的。static修饰成员方法最大的作用,就是可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗,我们可能会经常在帮助类中看到它的使用:

public class PrintHelper {public static void print(Object o){System.out.println(o);}public static void main(String[] args) {PrintHelper.print("Hello world");}
}

       上面便是一个例子(现在还不太实用),但是我们可以看到它的作用,使得static修饰的方法成为类的方法,使用时通过“类名.方法名”的方式就可以方便的使用了,相当于定义了一个全局的函数(只要导入该类所在的包即可)。不过它也有使用的局限,一个static修饰的方法中,不能使用非static修饰的成员变量和方法,这很好理解,因为static修饰的方法是属于类的,如果去直接使用对象的成员变量,它会不知所措(不知该使用哪一个对象的属性)。

 

3、静态块

       在说明static关键字的第三个用法时,我们有必要重新梳理一下一个对象的初始化过程。以下面的代码为例:

package com.dotgua.study;class Book{public Book(String msg) {System.out.println(msg);}
}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初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}

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

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

class Book{public Book(String msg) {System.out.println(msg);}
}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初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* static修饰的funStatic方法* **************** book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}

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

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

class Book{public Book(String msg) {System.out.println(msg);}
}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初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* static修饰的funStatic方法* **************** book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}

       我们将上一个例子稍微做了一下修改,可以看到,结果没有二致。

 

4、静态导包

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

/* PrintHelper.java文件 */
package com.dotgua.study;public class PrintHelper {public static void print(Object o){System.out.println(o);}
}
/* App.java文件 */import static com.dotgua.study.PrintHelper.*;public class App 
{public static void main( String[] args ){print("Hello World!");}/**Output* Hello World!*///~
}

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

 

三、总结

static是java中非常重要的一个关键字,而且它的用法也很丰富,主要有四种用法:

       1、用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;

       2、用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;

       3、静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;

       4、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 使用move来删除用户指定的文件

代码 #include <iostream>bool remove_file(std::string path){if (remove(path.c_str())0){std::cout << "success!" << std::endl;}else{std::cout << "False!" << std::endl;} } int main() {std::string path "/…

Java提高篇 —— Java关键字之final的几种用法

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

使用C++的方式实现AES算法

aes_file.h #include <iostream> #include <fstream> #include <bitset> #include <string> using namespace std; typedef bitset<8> byte; typedef bitset<32> word;const int Nr 10; // AES-128需要 10 轮加密 const int Nk 4; /…

Java提高篇 —— Java三大特性之封装

一、封装 封装从字面上来理解就是包装的意思&#xff0c;专业点就是信息隐藏&#xff0c;是指利用抽象数据类型将数据和基于数据的操作封装在一起&#xff0c;使其构成一个不可分割的独立实体&#xff0c;数据被保护在抽象数据类型的内部&#xff0c;尽可能地隐藏内部的细节&am…

sqlite3的backup和restore函数的使用

参考代码 第一段这个亲测可以使用 #include <sqlite3.h> #include <iostream> /* ** Perform an online backup of database pDb to the database file named ** by zFilename. This function copies 5 database pages from pDb to ** zFilename, then unlocks pD…

Java提高篇 —— Java三大特性之继承

一、前言 在《Think in java》中有这样一句话&#xff1a;复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言&#xff0c;仅仅能够复制代码并对加以改变是不够的&#xff0c;它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码…

Java提高篇 —— Java三大特性之多态

一、前言 面向对象编程有三大特性&#xff1a;封装、继承、多态。 封装&#xff1a;隐藏了类的内部实现机制&#xff0c;可以在不影响使用的情况下改变类的内部结构&#xff0c;同时也保护了数据。对外界而已它的内部细节是隐藏的&#xff0c;暴露给外界的只是它的访问方法。 继…

光盘刻录制作Ubuntu等操作系统的启动盘

前提条件 软媒刻录 空白光盘&#xff08;至少4.7G&#xff09;电脑&#xff08;最好使用外置的光驱&#xff09;系统镜像&#xff08;ISO格式&#xff09; 具体操作 打开软媒魔方选择光盘刻录按照标红的进行选择选择镜像->选择或者拖拽都可以选择刻录机->如果使用外部刻…

Java提高篇 —— 抽象类与接口

一、前言 接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。 抽象类与接口是java语言中对抽象概念进行定义的两种机制&#xff0c;正是由于他们的存在才赋予java强大的面向对象的能力。他们两者之间对抽象概念的支持有很大的相似&#xff0c;甚至可以互换&…

C++ const相关内容学习

const 作用 修饰变量&#xff0c;说明变量不可以被修改修饰指针&#xff0c;分为指向常量的指针&#xff08;pointer to const&#xff09;和自身是常量的指针&#xff08;常量指针&#xff0c;const pointer&#xff09;修饰引用&#xff0c;指向常量的引用&#xff08;refe…

Java提高篇 —— Java浅拷贝和深拷贝

一、前言 我们知道在Java中存在这个接口Cloneable&#xff0c;实现该接口的类都会具备被拷贝的能力&#xff0c;同时拷贝是在内存中进行&#xff0c;在性能方面比我们直接通过new生成对象来的快&#xff0c;特别是在大对象的生成上&#xff0c;使得性能的提升非常明显。然而我们…

openssl里面AES算法主要函数的参数的介绍

注意事项 使用API的时候&#xff0c;需要特别小心数据长度&#xff0c;一般没有指定长度的参数&#xff0c;默认都是16&#xff08;AES_BLOCK_SIZE&#xff09;个字节。输出数据的长度一般都是16字节的倍数&#xff0c;否则会出现数组越界访问。以下API中&#xff0c;encrypt表…

Java提高篇 —— Java内部类详解

一、简介 内部类是一个非常有用的特性但又比较难理解使用的特性。 内部类我们从外面看是非常容易理解的&#xff0c;无非就是在一个类的内部在定义一个类。 public class OuterClass {private String name ;private int age;public String getName() {return name;}public voi…

Ubuntu修改界面的大小

命令 xrandr 就会显示ubuntu支持的屏幕比例使用命令 xrandr --size 1680x1050 切换屏幕大小

Java提高篇 —— String缓冲池

一、String缓冲池 首先我们要明确&#xff0c;String并不是基本数据类型&#xff0c;而是一个对象&#xff0c;并且是不可变的对象。查看源码就会发现String类为final型的&#xff08;当然也不可被继承&#xff09;&#xff0c;而且通过查看JDK文档会发现几乎每一个修改String对…

C++最新使用开源openssl实现输入是文件,输出是文件的AES加解密的代码

AES.h头文件 #include <cstring> #include <fstream> #include <iostream> #include <openssl/aes.h>//AES文件加密函数 int aes_encrypt_file(const std::string &original_backup_file_path,const std::string &encrypted_file_path,const …

Java基础 —— JVM内存模型与垃圾回收

目录一、概述二、运行时数据区方法区运行时常量池堆栈本地方法栈程序计数器三、对象访问四、垃圾回收如何定义垃圾1、引用计数法2、可达性分析垃圾回收方法1、Mark-Sweep标记-清除算法2、Copying复制算法3、Mark-Compact标记-整理算法4、Generational Collection 分代收集垃圾收…

Report Design

ERP_ENT_STD-CSDN博客

规范化流程化提交自己代码到远程gitlab服务器

流程 进入到build目录使用make命令进行编译 make -j 6&#xff0c;前提是cmake命令已经执行../format.py rungit add .. 添加文件git checkout -b xxx 创建xxx分支&#xff0c;其中xxx是分支名字git branch 查看分支git commit -m "[chy/backup] modify parameters for…

Java提高篇 ——Java注解

目录一、注解注解的定义注解的应用元注解RetentionDocumentedTargetInheritedRepeatable注解的属性Java 预置的注解DeprecatedOverrideSuppressWarningsSafeVarargsFunctionalInterface二、注解的提取三、注解与反射四、注解的使用场景五、亲手自定义注解完成某个目的六、注解应…