笔记:static关键字

本文详细解释了Java中的静态变量(类变量)和实例变量的区别,包括它们的定义、特点、访问方式以及在类加载和对象创建中的行为。同时介绍了静态方法和非静态方法的应用,以及单例设计模式的两种常见实现方式——懒汉式和饿汉式。
摘要由CSDN通过智能技术生成

static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量。

一、静态变量(类变量)和非静态变量(实例变量)

1.类变量

定义:有static修饰,属于类,在计算机里只有一份,会被类的全部对象共享

特点:在类加载的时候就直接被加载到静态区中了,优于对象而存在

public class Student {
    //类变量一般定义为public,完全对外部进行共享,方便外部谁都可以对其进行访问和修改
    //如果定义成private,就只能在本类中共享
    public static String name;

    //实例变量
    public int age;
}

类变量的访问方式: 

①类名.类变量(访问本类中的类变量,可以省略类名不写;访问其他类中的类变量,必须带类名

public class Test {
    public static void main(String[] args) {
        //1.类名.类变量(推荐)
        Student.name="zhangsan";
    }
}

类的字节码文件(student.class)加载进来后,会在堆中额外开辟一个空间(暂称为"静态区")存放类变量。

根据类名去方法区里面找到该类,再根据该类找到类变量name,修改为"zhangsan"。

注:在jdk8之前,静态变量存在方法区;jdk8及以后,存放在堆中反射的class对象的尾部。

②对象.类变量

public class Test {
    public static void main(String[] args) {
        //2.对象.类变量(不推荐)
        Student s1=new Student();
        s1.name="lisi";

        Student s2=new Student();
        s2.name="wangwu";
        System.out.println(s1.name);//wangwu

        Student s3=null;
        //虽然s3指向空,但会变成Student.name去访问静态区中的name。
        System.out.println(s3.name);//wangwu
    }
}

当对象访问静态变量时,会直接通过类名来访问,而不是通过对象引用。

s1、s2、s3都会变成Student.name去访问静态区中的name,尽管s3指向空

由于类变量是所有对象共享的,所以最后静态区中的name被修改为"wangwu"。

应用场景:在开发中,如果某个数据只需要一份,且希望能被共享(访问、修改),则该数据可以被定义成类变量。

例如:系统启动后,要求用户类可以记住自己创建了多少个用户对象。

public class User {
    //类变量
    public static int number;

    //构造器
    public User(){
        //User.number++;
        //在同一个类中,访问自己的类变量,才可以省略类名不写
        number++;
    }
}
public class Test {
    public static void main(String[] args) {
        User u1 = new User();
        User u2 = new User();
        User u3 = new User();
        User u4 = new User();

        System.out.println(User.number);//4
    }
}

2.实例变量

定义:对象的变量 -->无static修饰,属于每个对象,即每个对象都有一份。

特点:在对象创建的时候才会产生

public class Student {
    //类变量
    public static String name;
    
    //实例变量
    public int age;
}

实例变量的访问方式:对象.实例变量 

public class Test {
    public static void main(String[] args) {
        Student s1=new Student();
        Student s2=new Student();

        s1.age=18;
        s2.age=23;
        System.out.println(s1.age);//18
        //System.out.println(Student.age);//报错
    }
}

实例变量是每个对象都有一份,数据各不同并不共享。所以最后s1.age仍是18。

二、静态方法(类方法)和非静态方法(实例方法)

1.类方法

 定义:有static修饰的成员方法,属于类

应用场景:工具类

public class Student {
    //类方法
    public static void printHelloWorld() {
        System.out.println("helloWorld");
    }
}

调用方式:

①类名.类方法(访问本类中的类方法,可以省略类名不写;访问其他类中的类方法,必须带类名)

public class Test {
    public static void main(String[] args) {
        //1.类名.类方法(推荐)
        Student.printHelloWorld();
    }
}

根据类名去方法区里面找该类,再调用其中的printHelloWorld方法

② 对象.类方法

public class Test {
    public static void main(String[] args) {
        //2.对象.类方法(不推荐)
        Student s1=new Student();
        s1.printHelloWorld();

        Student s2=null;
        //虽然s2指向空,但会变成Student.printHelloWorld()去调用该方法。
        s2.printHelloWorld();//成功执行
    }
}

当对象访问静态方法时,会直接通过类名来访问,而不是通过对象引用。

s1、s2都会变成变成Student.printHelloWorld()去调用该方法,尽管s2指向空

2.实例方法 

定义:对象的方法 --> 无static修饰的成员方法,属于对象

public class Stuedent {
    public static double score = 60;

    //实例方法(非静态方法/对象的方法)
    public void printPass() {
        //访问本类中的类变量,可以省略类名不写;访问其他类中的类变量,必须带类名
        System.out.println("成绩" + (score >= 60 ? "及格" : "不及格"));
    }
}

调用方式:对象.实例方法

public class Test {
    public static void main(String[] args) {
        Student s1=new Student();
        s1.printPass();//及格
        //Student.printPss();  //报错
    }
}

3.扩展(main方法的含义)

 main方法是测试类的类方法。

当用java命令执行Test程序,即java Test时,虚拟机事实上会直接调用Test.main(...)来触发main方法执行。

String[] args是以前由于接收键盘数据的,现在没用,但是java为了兼容低版本,继续保留了。

例如:

4.工具类

定义:帮助我们做一些事情的类,但是不描述任何事物的类。

工具类中的方法都是一些类方法,每个方法都是用来完成一个功能的,工具类是给开发人员使用的。

好处:提高了代码复用;调用方便,提高了开发效率

首先定义一个简单的Student类

public class Student {
    private String name;
    private int age;
    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

再定义一个StudentUtil工具类,实现求学生最大年龄的方法。

public class StudentUtil {
    //私有化构造方法
    private StudentUtil(){};

    //方法需要定义成类方法(静态的),
    public static int getMaxAge(ArrayList<Student> list){
        int max=list.get(0).getAge();
        for (int i = 1; i < list.size(); i++) {
            int tempAge=list.get(i).getAge();
            if(max<tempAge){
                max=tempAge;
            }
        }
        return max;
    }

}

注:

①私有化构造方法-->不让外界创建他的对象(外界需要使用该类的方法,没必要创建该类的对象)

②方法需要定义成类方法(静态的),直接用类名调用即可,调用方便,也能节省内存。

原因:如果定义成实例方法,想调用就必须要创建对象,对象占内存,会浪费内存;并且工具类也不能创建对象。

测试类:

public class Test {
    public static void main(String[] args) {
        //1.创建集合
        ArrayList<Student> list=new ArrayList<>();
        //2.创建对象
        Student s1=new Student("张三",23);
        Student s2=new Student("李四",24);
        Student s3=new Student("王五",25);
        //3.将学生添加到对象中
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //调用工具类中的方法
        int maxAge=StudentUtil.getMaxAge(list);
        System.out.println(maxAge);//25
    }
}

三、static的注意事项

1.静态方法只能访问静态

 类方法中可以直接访问类的成员(类变量和类方法),不可以直接访问实例成员(实例变量和实例方法)。

public class Student {
    static String schoolName;//类变量
    double score;//实例变量

    //类方法
    public static void print1(){
        //注意:同一个类中,访问类成员,可以省略类名不写
        //类成员
        schoolName="heima";
        show1();

        //实例成员
        //System.out.println(score);    报错
        //print2();     报错
    }
    //类方法
    public static void show1(){

    }

    //实例方法
    public void print2(){

    }
}

2.非静态方法可以访问所有

 实例方法中既可以直接访问类成员,也可与直接访问实例成员。

public class Student {
    static String schoolName;//类变量
    double score;//实例变量


    //实例方法
    public void print2(Student this){
        //类成员
        schoolName="heima";
        show1();
        
        //实例成员
        System.out.println(score);
        show2();
    }

    //实例方法
    public void show2(){

    }
    
    //类方法
    public static void show1(){

    }

}

3.静态方法中没有this关键字

 实例方法中可以出现this关键字,类方法中不可以出现this关键字。

this:表示当前方法调用者的地址值

public class Student {
    static String schoolName;//类变量
    double score;//实例变量

    //类方法
    public static void print1(){
        //System.out.println(this);     报错
    }

    //实例方法
    public void print2(Student this){
        System.out.println(this);
        //以下this均可省略不写,都时自动隐含的
        System.out.println(this.schoolName);
        System.out.println(this.score);

        this.print1();
        this.show2();
    }

    //实例方法
    public void show2(){

    }
}

注:

实例方法的形参当中实际上隐含了一个Student this,这个this不能手动赋值,是由jvm自动赋值的。类方法形参中并没有隐含Student this。

所以实例方法中调用的变量和其他方法前面都相当于有个隐含的this.,但是由于没有和形参中其他变量名字重复,可以省略不写。

简单理解:this代表当前对象,实例方法是对象的方法,由于是对象调用,this就指代这个对象。类方法无论是类名还是对象调用,最终都会转变成类名调用,没有对象,也就没有this。

四、代码块

1.局部代码块

定义:写在方法中的单独的一对{}

作用:提前结束变量的生命周期,节约内存

public class Test {
    public static void main(String[] args) {
        {
            int a = 10;
        }
        //当代码执行到这里时,变量a就已经结束生命周期,从内存中消失了
        //System.out.println(a);    报错
    }
}

2.实例代码块/构造代码块

定义:每次创建对象时,执行实例代码块,并在构造器前执行

作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值(不推荐)

public class Student {
    static String schoolName;//类变量
    double score;//实例变量

    {
        System.out.println("实例代码块执行了~~");
    }

    public Student() {
        System.out.println("无参构造器执行了~~");
    }

    public Student(double score) {
        System.out.println("有参构造器执行了~~");
    }
public class Test {
    public static void main(String[] args) {
        Student s1=new Student();
        Student s2=new Student(23);
    }
}

运行结果:

可以看出,每次创建对象,都在构造器之前执行了一次实例代码块

应用场景:例如记录对象创建的日志,原本需要在所有构造器中都要写的重复代码,只需要在实例代码块中写一次。可以把多个构造方法中重复的代码抽取出来,提高代码复用性

初始代码:

public class Student {
    double score;//实例变量

    public Student() {
        System.out.println("有人创建了对象:"+this);
    }

    public Student(double score) {
        System.out.println("有人创建了对象:"+this);
    }
}

优化后代码:

public class Student {
    double score;//实例变量

    {
        System.out.println("有人创建了对象:"+this);
    }

    public Student() {
    }

    public Student(double score) {
    }

由于采用构造代码块的话,每个构造方法执行时都必须会执行构造代码块中的内容,不够灵活。

改进方式: 

方法一:将重复代码抽取到一个构造方法中,在其他构造方法中用this关键字调用该构造方法

 

方法二 :将重复的代码抽取成一个方法,在构造方法中需要使用时直接调用

3.静态代码块

定义:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次

作用:完成类的初始化,例如:对类变量的初始化赋值

public class Student {
    static String schoolName;//类变量
    double score;//实例变量

    static {
        schoolName="zhangsan";
        System.out.println("静态代码块执行了~~");
    }
}
public class Test {
    public static void main(String[] args) {
        System.out.println(Student.schoolName);
        System.out.println(Student.schoolName);
        System.out.println(Student.schoolName);
    }
}

运行结果: 

可以看出静态代码块只在类加载的时候执行了一次。

注:虽然也可以通过声明时直接初始化赋值的方式,但如果所定义的类变量是一个复杂的对象,就不方便直接初始化了,此时就需要在静态代码块中初始化

举个🌰🌰🌰,初始化用户信息

public class App {
    static ArrayList<User> list = new ArrayList<>();
    
    static {
        list.add(new User("张三","123456","100101200101011234","13112344321"));
        list.add(new User("李四","123123","100101200101014321","13112341234"));
        list.add(new User("王五","123321","100101200101011212","13112345678"));
    }

    public static void main(String[] args) {
        //....
    }    
}

对于集合这种复杂对象信息,就没法在声明的时候直接初始化赋值。虽然可以在main函数中实现该操作,但只要是函数,都有可能被多次调用

如果创建对象连接的是数据库,那么每次启动该系统执行main函数时,都会重复执行创建对象的代码。 

为了只让这些代码执行一次,最好的办法就是放在静态代码块中执行。

 

五、单例设计模式(确保一个类只有一个对象)

1.饿汉式单例

 定义:拿对象时,对象早就创建好了

应用场景:如果一个对象需要频繁调用,就采用饿汉式单例

public class User {
    //2,定义一个类变量记住类的一个对象
    private static User u=new User();
    //1.私有化构造器
    private User(){

    }

    //3.定义一个方法返回类的对象
    public static User getObject(){
        return u;
    }
}
public class Test {
    public static void main(String[] args) {
        User u1=User.getObject();
        User u2=User.getObject();
        System.out.println(u1==u2);//true
    }
}

2.懒汉式单例

 定义:拿对象时,才开始创建对象

应用场景:如果一个对象不需要经常调用,就采用懒汉式单例,节省内存开销

public class User {
    //2,定义一个类变量记住类的一个对象
    private static User u;
    //1.私有化构造器
    private User(){

    }

    //3.定义一个方法返回类的对象
    public static User getInstance(){
        if(u==null){
            u=new User();
        }
        return u;
    }
}
public class Test {
    public static void main(String[] args) {
        User u1=User.getInstance();
        User u2=User.getInstance();
        System.out.println(u1==u2);//true
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值