Java中static、final关键字【详解】
========================
一、static关键字
===========
- static是静态的意思,可用来修饰 成员方法、成员变量。static修饰后的变量、方法,可以被类的所有对象共享
- static修饰成员变量之后,这个变量被称为类变量或静态成员变量;无static修饰的成员变量是属于每个对象的,这个变量被称为实例变量
- static修饰方法之后,这个方法被称为类方法或静态方法;无static修饰的成员方法是属于每个对象的,这个成员方法也叫做实例方法
1.1 成员变量
成员变量分类
- 静态成员变量:有static修饰,属于类、加载一次、内存中只有一份。表示公司名称、在线人数等需要被类的所有对象共享的信息
- 实例成员变量:无static修饰,属于每个对象。譬如user类的name、age
1.1.1 静态变量及其访问
静态成员变量(被static修饰,属于类、加载一次、内存中只有一份)。直接用 类名 访问即可,因为类只有一个,所以静态成员变量在内存区域中也只有一份,所有的对象都可以共享该变量。
- 定义格式
修饰符 static 数据类型 变量名 = 初始值;
- 访问
类名.静态成员变量(推荐)
对象.静态成员变量(不推荐)
- 举例:现在需要定义abc公司全部的员工,这些员工类的对象的公司属性应该都是“abc”,这个时候就可以将公司属性定义为static修饰的静态成员变量
public class Employee {
//静态成员变量,属于类,只有一份
public static String companyName = "abc";
//实例变量
private String name;
private Integer age;
//...
}
//访问
public static void main(String[] args) {
System.out.println(Employee.companyName); //abc
Employee.companyName = "learn";
System.out.println(Employee.companyName); //learn
Employee employee1 = new Employee("zhangsan", 18);
System.out.println(employee1.getAge()); //18
}
1.1.2 实例变量及其访问
无static修饰的成员变量
注意:实例成员变量属于每个对象,必须创建类的对象才能访问
访问:对象.实例成员变量
1.2 成员方法
成员方法分类
- 静态成员方法:有static修饰,属于类、内存中只有一份。
- 实例成员方法:无static修饰,属于每个对象,只能用对象访问。
表明对象自己的行为的、且方法中需要访问实例成员的,该方法必须声明为实例方法。
如果该方法是以执行一个共有功能为目的,可以声明为静态方法。
1.2.1 静态方法及其访问
静态方法(被static修饰,属于类、内存中只有一份)。直接用 类名访问即可。因为类只有一个,所以静态方法在内存区域中也只存在一份。所有的对象都可以共享这个方法。
- 定义格式
修饰符 static 返回类型 方法名(param) {}
- 访问
类名.静态方法(推荐)
对象.静态方法(不推荐)
- 举例
public class Employee {
public static String companyName = "abc";
private String name;
//...
public static void work() {
System.out.println("我们都在" + companyName + "公司工作");
}
public void achive() {
System.out.println(name + "实现大成就");
}
}
public static void main(String[] args) {
Employee employee1 = new Employee("zhangsan", 18);
Employee.work();
//Employee.achive(); //报错,必须使用对象范围
employee1.achive();
}
1.2.2 实例方法及其访问
无static修饰的成员方法
注意:实例方法是属于每个对象,必须创建类的对象才可以访问
访问:对象.实例方法
1.3 小结
- 1)当static修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
- 2)无static修饰的成员变量或者成员方法,称为实例变量、实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
-
3)static访问注意事项
- 静态方法只能访问静态成员。不能直接访问实例成员
- 实例方法可以访问静态成员,也可以访问实例成员
- 静态方法中是不可以出现this关键字的。this指当前对象,静态方法中不用声明实例对象
在java中,程序执行时 类的字节码文件会被加载到内存中,如果类没有创建对象 类的成员变量则不会分配到内存;但对于被static修饰的静态变量/方法,堆中有一个专属的静态变量区,当JVM虚拟机将字节码加载到内存时,会为静态变量/方法在堆中分配出一段空间用于放置值,即静态变量/方法跟随类加载而加载
当你通过类定义对象的时候,才会在堆内存中为不同的对象分配空间。
(由于静态方法已经存在内存中,而非静态方法不存在,使用一个存在的方法去调用一个不存在的方法,不可行,故静态方法无法访问实例成员)
若想要使用静态方法调用非静态方法,只需要实例化new一个类对象,此时jvm虚拟机就会为对象开辟一块内存,该类的所有方法也随之被存储到内存当中。此时静态方法和非静态方法都在内存当中,所以在静态方法中可以使用对象去调用一个非静态方法。
- 4)static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以可被所有对象共享。
- 5)无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
- 6)内存原理
1.4 static应用知识
- 工具类
1)概述:类中都是一些静态方法,每个方法都是以完成一个共用的功能为目的,这个类用来给系统开发人员共同使用。
2)由于工具里面都是静态方法,直接用类名即可访问,因此,工具类无需创建对象,建议将工具类的构造器进行私有。
- 静态代码块
1)代码块概述:代码块是类的5大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。在Java类下,使用 { } 括起来的代码被称为代码块 。
2)静态代码块:static{}。特点——需要通过static关键字修饰,随着类的加载而加载,并且自动触发、只执行一次。使用场景——在类加载的时候做一些静态数据初始化的操作,以便后续使用
- 单例设计模式
1)单例模式:可以保证系统中,应用该模式的这个类永远只有一个实例,即一个类永远只能创建一个对象。例如任务管理器对象我们只需要一个就可以解决问题,节省内存空间
2)饿汉单例模式:在用类获取对象的时候,对象已经提前为你创建好了
设计步骤:
(1)定义一个类,把构造器私有
(2)定义一个静态变量存储一个对象
public class SingleInstance {
public static SingleInstance instance=new SingleInstance();
private SingleInstance() {
System.out.println("创建了一个对象");
}
}
3)懒汉单例模式:在真正需要该对象的时候,才去创建一个对象(延迟加载对象)
设计步骤:
(1)定义一个类,把构造器私有
(2)定义一个静态变量存储一个对象
(3)提供一个返回单例对象的方法
public class SingleInstance {
private static SingleInstance instance;
public static SingleInstance getInstance(){
if (instance==null){
instance=new SingleInstance();
}
return instance;
}
}
二、final关键字
当我们提供了一个父类,子类可以通过方法重写 在父类的基础上改写父类内容。如果有个方法 不希望别人修改,怎么解决呢?
Java提供了final关键字,表示修饰的内容不可变。
final:最终的、不可改变的。可用于修饰类、方法、变量。
- 类:被修饰的类,不能被继承
- 方法:被修饰的方法,不能被重写
- 变量:被修饰的变量,有且只能被赋值一次
2.1 修饰类:不能被继承
final修饰的类 不能被继承,即当前这个类不能有任何的子类。格式如下
final class 类名 {
}
注意:一个类如果被final修饰,那么其中所有的成员方法都无法进行覆盖重写,因为无子类。
查看API发现,像 public final class String
、public final class Math
、public final class Scanner
等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
final class Fun1 {
}
// class A extends Fun1 {} // 报错,不能继承final的类
2.2 修饰方法
final修饰的方法,不能被重写。格式如下:
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
注意:对于类、方法来说,abstract关键字和final关键字不能同时使用。有抽象方法的abstract类被继承时,其中的方法必须被子类Override,而final不能被Override,互相矛盾。
public class Fun2 {
final public void show1() {
System.out.println("Fun2 show1");
}
public void show2() {
System.out.println("Fun2 show2");
}
}
public class SonFun2 extends Fun2{
//重写final方法 会报错
// @Override
// final public void show1() {
// System.out.println("Fun2 show1");
// }
@Override
public void show2() {
System.out.println("Fuu2 show2");
}
}
2.3 修饰变量-局部变量
2.3.1 局部变量——基本类型
基本类型的局部类型,被final修饰后,只能赋值一次,不能再修改,“一次赋值,终身不变”。代码如下
public class FinalVariable {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 18;
// 第二次赋值
a = 24; // 报错,不可重新赋值
// 声明变量,直接赋值,使用final修饰
final int b = 18;
// 第二次赋值
b = 24; // 报错,不可重新赋值
}
}
2.3.2 局部变量——引用类型
引用类型被final修饰后,地址值不能改变,否则报错。
虽然地址值不能变,但内容可以变(地址所指向的对象可以变)
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1)引用类型 无final修饰,地址不同
public static void main(String[] args) {
Person p1 = new Person("zhangsan");
System.out.println(p1); //打印地址 xx.Person@5c8da962
System.out.println(p1.getName()); //zhangsan
p1 = new Person("lisi");
System.out.println(p1); //打印地址 xx.Person@512ddf17
System.out.println(p1.getName()); //lisi
}
2)被final修饰,地址值不可改变,否则报红。但是可以修改对象内容
final Person p2 = new Person("Jenny");
//错误写法。final修饰引用类型变量,其中的地址不可改变
//p2 = new Person("Jack");
System.out.println(p2.getName()); //Jenny
p2.setName("Jack"); //修改对象内容
System.out.println(p2.getName()); //Jack
2.4 修饰变量-成员变量
成员变量如果被final修饰,一旦有了初始值 就不能被重新赋值。
由于成员变量具有默认值,用final关键字修饰后 不会再给默认值,必须手动赋值,否则会报错。因此必须手动初始化,有两种初始化方式——显示初始化和构造方法初始化。
对于final类型的成员变量,有两种初始化方式(赋值方式),显示初始化和构造方法初始化,只能选其中一个,且不需要setXX函数。
- 显示初始化:在定义成员变量的时候里面赋值(常用)
public class Person {
private final String name = "Jenny";
}
- 构造方法初始化:必须保证所有重载的构造方法,都会对final修饰的成员变量进行赋值,即每个构造方法中都要赋值一次(不常用、了解即可)
public class Person {
//对于final类型的成员变量,有两种初始化方式(赋值方式),显示初始化和构造方法初始化,只能选其中一个
// 定义成员变量时指定默认值,合法。
final String name = "Jenny";
final int num;
public Person() {
// 在构造器中分配初始值
num = 20;
}
public Person(int num) {
// 在构造器中分配初始值
this.num = num;
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
}
被final修饰的常量名称,一般都有书写规范,所有字母都大写。
原文链接: https://juejin.cn/post/7350185416652210176
文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17793.html