修饰符-static
Java是由C/C++泛生的,其也保留了C/C++的部分特性,如关键字。在C/C++中,关键字有着特殊的含义。
1.static修饰符
在日常使用中关键字static
一般用来修饰类的属性和方法,而被static修饰的成员会被称为静态成员或类成员。因为static修饰的成员它们属于类,不属于任何的对象。静态成员可以使用类名来访问,也可以通过对象来访问,className.staticVar
或className.methods()
与非静态成员不同,使用static修饰的静态成员是在类初始化时就加载出来的,而非静态成员是在创建类的实例时new className()
才加载出来的,类的初始化早于类的实例。对类而言,生命周期随着类一同加载,也随着类一同消失,静态成员是类实例之间共享的
- 类的初始化:类加载分为三个步骤:加载,连接,初始化。在加载阶段,会根据类的全限定名来读取该类的二进制字节流到JVM;到连接阶段,先对这些二进制的字节流进行验证(文件格式验证、元数据验证、字节码验证和符号引用验证),再为类的静态成员分配内存和初始值;到初始化阶段,会给在连接阶段中的静态成员,再次赋值,这次赋值会根据程序员自己写的逻辑去初始化类变量和其他资源
- 类的实例化:主要还是通过
new className()
对类进行实例化。这个过程会在内存中开辟空间,将非静态的成员存放进去- 初始化只在类加载的时候执行一次。但实例化可以有多次,也就是可以创建多个对象,每创建一次就开辟一块内存空间
1.1static变量
public class Student{
private static int numID = 0;
int id;
int age;
String name;
public Student(int age, String name){
id = ++numID;
this.age = age;
this.name = name;
}
public String toString(){
return "id:" + id + ",Name:" + name + ",age" + age;
}
}
public static void main(String[] args){
Student lise = new Student(21,"lise");
Student Tim = new Student(33,"Tim");
System.out.println(Tim.toString());//id:2,Name:Tim,age:33
System.out.println(lise.toString());//id:1,Name:lise,age:21
}
解析:numID是由static修饰的,所以它是被类实例之间共享的。每次创建Student对象时,其构造器就都会执行一次,numID也就会+1,这样numID也就记录也Student类被实例的次数
1.2static方法
public class Student{
static int count = 0;
int num;
public Student(){
++count;
}
public static String go(){
return "count = "+ count +",num = "+num+" ,is Student a go!!";
}
public static int getNum(){
return num; //编译报错
}
public int getCount(){
return count;
}
}
public static void main(String[] args){
Student student = new Student();
System.out.println(Student.go());//count = 1 ,is Student a go!!
System.out.println(student.getCount());//1
}
Student类中,
go()
、getID()
方法被static
修饰,go()
可以访问非静态成员也可以访问静态成员,但getNum()
方法它不能直接访问非静态成员,因为在Student类初始化时,num
变量还没被定义,没有到num加载的步骤,静态成员无法在内存中找到未定义的成员;getCount()
是在Student类初始化后被定义出来的,静态成员count
在初始化时已经被加载了,所以它可以从内存中找到静态成员count
。
go()
方法被static
修饰,在main()方法中无需创建对象去调用go(),只需导入该类所在的包就可以通过className.methods()
来调用静态成员早于非静态成员加载,也就是为什么静态成员和非静态成员不能直接相互访问
1.3static代码块
class Book{
public Book(String msg) {
System.out.println(msg);
}
}
public class Person {
{
Book book5 = new Book("book5成员变量初始化");
}
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初始化");
Person p2 = new Person("p2初始化");
}
/*static成员book2成员变量初始化
static成员book4成员变量初始化
static修饰的funStatic方法
****************
book5成员变量初始化
book1成员变量初始化
book3成员变量初始化
p1初始化
book5成员变量初始化
book1成员变量初始化
book3成员变量初始化
p2初始化
Process finished with exit code 0
*/
}
由代码的输出顺序可知,类的初始化是从static成员开始,再到普通成员,最后再到构造方法。初始化完到实例化,实例化则按代码的顺序由上到下执行。
static修饰的类成员,在程序运行过程中,只需要初始化一次即可,不会进行多次的初始化。
1.4静态导包
package com.hello.text;
public class Person{
public static String toString(){
return "null";
}
}
import static com.hello.text.Person.*;
public class AppText(){
public static void main(String[] args){
System.out.println(toString());//null
}
}
静态导包就是java包的静态导入,用import static
代替import
静态导入包是JDK1.5中的新特性。Person包含了toString方法,在AppText中使用了import static
导入了Person类,在引入时,在类名最后加上.*
是将该类所有方法直接导入,但不建议使用,因为当某个大型的项目在运行时,如果把需要用到的外部方法导入时,每次都要导入这个外部类的所有资源会消耗大量的内存和时间。用import static
导入,在需使用使用Person类中方法时无需使用className.methods()
,直接用methods()
就可以调用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下