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,一经查实,立即删除!

相关文章

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

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

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

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

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

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

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

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

Java提高篇 —— String缓冲池

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

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

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

Report Design

ERP_ENT_STD-CSDN博客

Java提高篇 ——Java注解

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

Android 性能优化四个方面总结

目录一、四个方面二、卡顿优化1、Android系统显示原理2、卡顿根本原因3、性能分析工具(1)Profile GPU Rendering(2)TraceView(3)Systrace UI 性能分析4、优化建议(1)布局优化&#x…

Android 开源框架选择

目录一、前言二、APP的整体架构三、技术选型的考量点四、日志记录能力五、JSON解析能力1、gson2、jackson3、Fastjson4、LoganSquare六、数据库操作能力1、ActiveAndroid2、ormlite3、greenDAO4、Realm七、网络通信能力1、android-async-http2、OkHttp3、Volley4、Retrofit八、…

Android Studio 自定义Gradle Plugin

一、简介 之前公司的一个项目需要用到Gradle插件来修改编译后的class文件,今天有时间就拿出来整理一下,学习一下Gradle插件的编写还是一件十分有意义的事。 二、Gradle插件类型 一种是直接在项目中的gradle文件里编写,这种方式的缺点是无法复…

Android Studio Gradle两种更新方式

第一种、Android Studio自动更新 第一步:修改gradle版本 修改项目根目录/gradle/wrapper/gradle-wrapper.properties最后一行的地址: distributionUrlhttps://services.gradle.org/distributions/gradle-3.3-all.zip新gradle地址从官方下载的地方有。…

Android Canvas的drawText()和文字居中方案

自定义View是绘制文本有三类方法: // 第一类 public void drawText (String text, float x, float y, Paint paint) public void drawText (String text, int start, int end, float x, float y, Paint paint) public void drawText (CharSequence text, int start…

IntelliJ IDEA配置Tomcat

查找该问题的童鞋我相信IntelliJ IDEA,Tomcat的下载,JDK等其他的配置都应该完成了,那我直接进入正题了。 1、新建一个项目 2、由于这里我们仅仅为了展示如何成功部署Tomcat,以及配置完成后成功运行一个jsp文件,我仅勾…

Android开发之Path详解

目录一、xxxTo方法1、lineTo(float x, float y)2、moveTo(float x, float y)3、arcTo3.1、arcTo(RectF oval, float startAngle, float sweepAngle)3.2、arcTo(RectF oval, float startAngle, float sweepAngle,boolean forceMoveTo)3.3、arcTo(float left, float top, float r…

Android APK打包流程

目录一、概述二、打包流程1、打包资源文件,生成R.java文件2、处理aidl文件,生成相应的Java文件3、编译项目源代码,生成class文件4、转换所有的class文件,生成classes.dex文件5、打包生成APK文件6、对APK文件进行签名7、对签名后的…

Android Studio 安装ASM插件

目录一、安装步骤1、Android Studio -> Preferences...2、Plugins -> Browse repositories...3、搜索ASM -> 选中要安装的插件 -> 右侧点击Install4、安装完后点击Restart Android Studio5、Android Studio重启后右侧会有个ASM图标6、找一个类右键 -> Show Byte…

使用openssl完成aes-cbc模式的数据加解密,输入和输出都是字符串的形式

代码 #include <cstring> #include <memory>#include <openssl/aes.h> #include <openssl/md5.h>namespace hsm{namespace mgmt{void get_md5_digest(const std::string &data,uint8_t result[16]){MD5_CTX md5_ctx{};MD5_Init(&md5_ctx);MD5…

Android ViewRoot、DecorViewWindow浅析

目录简介目录1、VeiwRoot1.1、简介1.2、特别注意2、DecorView2.1、定义2.2、作用2.3、特别说明3、Window4、Activity5、之间关系5.1、总结5.2、之间的关系简介 DecorView为整个Window界面的最顶层View。DecorView只有一个子元素为LinearLayout。代表整个Window界面&#xff0c;…

Android 为控件设置阴影

在Android中设置一个阴影很简单&#xff0c;只需要两步&#xff1a; 设置eleavation值&#xff08;高度&#xff09;添加一个背景或者outline &#xff08;即阴影的形状&#xff09; 说明&#xff1a; View的大小位置都是通过x&#xff0c;y确定的&#xff0c;而现在有了z轴的…