Day35--static关键字详解
Day35--static关键字详解
本次要掌握的是:
1.静态变量、非静态变量 调用变量的方法
2.静态方法、非静态方法 调用方法的方法
3.静态代码块和匿名代码块、构造器执行的先后顺序
示例:
在demo07中,打开Student类
建立main方法,创建age类变量和score实例变量;创建Student的实例s1,用对象调用变量age、score;用类调用变量age、score
package com.liu.oop.demo07;
//static
public class Student {
private static int age; //静态的变量------类变量
private double score; //非静态的变量----实例变量
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.age); //通过对象调用变量
System.out.println(Student.age);//通过类调用变量
System.out.println(s1.score);
System.out.println(Student.score);//报错
}
}
调用变量的两种方式:
1.只能通过实例对象调用变量------------------------------------实例变量
2.既能通过类,又能通过实例对象调用变量-----------------类变量
上面的代码,为什么对变量age,通过对象调用和通过类调用都可以,而对变量score,不能通过类调用?
详细解释;
一、关于静态变量
在 Java 中,静态变量(用static关键字修饰)的,既可以通过类名Student来访问它(如Student.age),也可以通过创建的对象来访问(如s1.age,这里s1是Student类的一个实例对象)
二、关于非静态变量(实例变量)
非静态变量(没有用static关键字修饰的变量)必须通过创建对象来访问。
再比如:创建了非静态方法run和静态方法go,如何在下面的代码main方法中调用他们?
package com.liu.oop.demo07;
//static
public class Student {
private static int age; //静态的变量
private double score; //非静态的变量
public void run(){}
public static void go(){}
public static void main(String[] args) {
}
}
实际方法:
package com.liu.oop.demo07;
//static
public class Student {
private static int age; //静态的变量
private double score; //非静态的变量
public void run(){} //静态方法
public static void go(){} //非静态方法
public static void main(String[] args) {
//调用 go方法
student.go();
Student.go();
go();
//调用run方法
student.run();
new Student().run();
}
}
以下是对 new Student().run();这种调用 run 方法的方式的详细介绍:
在 Java 中,要调用一个非静态方法(像示例代码中的 run 方法),通常需要先创建类的实例(对象),然后通过该实例来调用方法。
Student student = new Student(); // 创建Student类的一个实例
student.run(); // 通过创建好的实例调用run方法
new Student().run(); 这种写法将对象的创建(实例化)和对非静态方法 run 的调用合并在了一起。它首先使用 new Student() 创建了 Student 类的一个新对象,然后紧接着通过这个刚创建好的对象调用了 run 方法。不过,需要注意的是,如果在后续代码中还可能会多次使用到同一个 Student 对象,那么更推荐使用常规的先创建对象并保存引用,然后再通过引用多次调用对象方法的方式,这样代码的可读性和可维护性会更好。
与此同时,我们发现了一个有趣的事情:
非静态方法可以调用静态方法,但是静态方法不能调用非静态方法
public void run(){
go();//可以
}
public static void go(){
run();//报错
}
在类里面,有代码块
package com.liu.oop.demo07;
public class Person {
{
//代码块(匿名代码块)
}
static {
//静态代码块
}
}
我们可以借此了解代码块的相关信息
清空Person的内容,然后创建匿名代码块,里面输出System.out.println("匿名代码块");
创建静态代码块,System.out.println("静态代码块");
创建无参构造器,输出 System.out.println("构造方法");
然后创建main方法,创建Person的实例对象
猜一猜,下面的代码输出结果是什么?
package com.liu.oop.demo07;
public class Person {
{
System.out.println("匿名代码块");
//代码块(匿名代码块)
}
static {
System.out.println("静态代码块");
//静态代码块
}
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
new Person();
}
}
上面的代码加载过程:
- Java 程序启动并执行包含
main方法的类(在这里是Person类)时,Java 虚拟机(JVM)开始进行类加载过程。静态代码块是在类加载阶段初始化类时执行的。这是因为静态代码块通常用于初始化静态变量的默认值等。 - 在类加载完成后,当执行
new Person();来创建一个Person类的对象时,首先会执行实例初始化块(匿名代码块)。 - 完成匿名代码块的执行后,接着会执行构造器(
public Person()),完成对象的创建和初始化过程。
代码输出结果是:
静态代码块
匿名代码块
构造方法
代码第一次执行,按照先后顺序来看,
首先执行的是:静态代码块
然后执行的是:匿名代码块
最后执行的是:构造器
也就是说,在创建对象的时候,先执行静态代码块,再执行匿名代码块,最后执行构造器
那么,再执行一次,结果会有变化吗?
package com.liu.oop.demo07;
public class Person {
//2
{
System.out.println("匿名代码块");
//代码块(匿名代码块)
}
//1
static {
System.out.println("静态代码块");
//静态代码块
}
//3
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println("=========================================");
Person person2 = new Person();
}
}
输出结果:
静态代码块
匿名代码块
构造方法
=========================================
匿名代码块
构造方法
可见,静态代码块只执行一次
再一个例子:
想生成随机数,我们可以这样:
package com.liu.oop.demo07;
public class Test {
public static void main(String[] args) {
System.out.println(Math.random());
}
}
但是,能不能直接System.out.println(random()); 不用Math?
可以!!!
导入
package com.liu.oop.demo07;
//静态导入包
import static java.lang.Math.random;
public class Test {
public static void main(String[] args) {
System.out.println(random());
}
}
拓展:
如果类是被final修饰的,他没有子类
假如Person是被final修饰的,想让Test作它的子类,不行
package com.liu.oop.demo07;
public final class Person
public class Test extends Person{ //报错
错误示例:
package com.liu.oop.demo07;//在demo07中,打开Student类
//
// 建立main方法,创建age类变量和score实例变量;创建Student的实例s1,用对象调用变量age、score;用类调用变量age、score
public class Student {
public static int age;
public int score;
public static void main(String[] args) {
Student s1 = new Student();
s1.age(); //报错
s1.score(); //报错
Student.age(); //报错
Student.score();//报错
}
}
上面的代码为什么报错?
以下是这段代码报错的原因分析:
1. 变量调用方式错误
在Java中,对于实例变量(非静态变量,像代码中的score),需要通过实例对象来访问,其访问语法是实例对象.实例变量名。而对于静态变量(像代码中的age),既可以通过类名直接访问(类名.静态变量名),也可以通过实例对象来访问(实例对象.静态变量名,不过这种方式不推荐,容易造成混淆)。
在代码的main方法里:
s1.age();这一行报错,是因为age虽然是静态变量,但这样的调用方式像是在调用一个方法,而它实际只是一个变量,正确的访问方式如果通过实例对象来访问应该是s1.age(直接获取它的值或者对其进行赋值等操作,例如s1.age = 10;),或者通过类名访问即Student.age。s1.score();报错是因为同样把变量当成方法在调用了,对于实例变量score,正确的访问应该是类似s1.score = 80;或者获取它的值如System.out.println(s1.score);等这样的操作,而不是加括号像调用方法一样去使用它。Student.score();也报错,原因和前面把变量当成方法调用类似,静态方式访问实例变量本身就是错误的,实例变量必须通过实例对象来访问,不能通过类名去访问它,因为它是和具体的对象实例相关联的,不像静态变量属于类这个层面,和具体某个对象实例无关。
2. 修改后的正确代码示例
以下是修正后的代码示例,展示了正确的变量访问方式:
package com.liu.oop.demo07;
public class Student {
public static int age;
public int score;
public static void main(String[] args) {
Student s1 = new Student();
// 通过实例对象s1访问实例变量score,进行赋值操作
s1.score = 90;
// 通过实例对象s1访问静态变量age,进行赋值操作
s1.age = 20;
// 通过类名访问静态变量age,输出其值
System.out.println(Student.age);
// 通过实例对象s1访问实例变量score,输出其值
System.out.println(s1.score);
}
}
总之,代码报错主要是混淆了变量和方法的使用语法,以及没有正确遵循Java中静态变量和实例变量不同的访问规则所导致的。

浙公网安备 33010602011771号