掘金 后端 ( ) • 2024-04-22 16:59

一、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;
    }
}

参考https://blog.csdn.net/Javascript_tsj/article/details/127163683

二、final关键字

当我们提供了一个父类,子类可以通过方法重写 在父类的基础上改写父类内容。如果有个方法 不希望别人修改,怎么解决呢?

Java提供了final关键字,表示修饰的内容不可变。

final:最终的、不可改变的。可用于修饰类、方法、变量。

  • 类:被修饰的类,不能被继承
  • 方法:被修饰的方法,不能被重写
  • 变量:被修饰的变量,有且只能被赋值一次

2.1 修饰类:不能被继承

final修饰的类 不能被继承,即当前这个类不能有任何的子类。格式如下

final class 类名 {
}

注意:一个类如果被final修饰,那么其中所有的成员方法都无法进行覆盖重写,因为无子类。

查看API发现,像 public final class Stringpublic final class Mathpublic 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修饰的常量名称,一般都有书写规范,所有字母都大写