static静态变量
我们都知道在测试文件中都需要有:
1
| public static void main(String[] args)
|
这段文字,而文字中public void main这几个我都已经数值,但是这个static是什么意思呢?今天我们来一起探讨一下这个词语。
属性静态调用
当我们遇到不同的成员需要有相同的属性的时候,为了方便同时输入和调用属性,我们引入了static(静态)修饰符:
user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.bayeeaa.demo1.d9_static;
public class user { private String name; private int number; public static String tea;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
public user(String name, int number) { this.name = name; this.number = number; }
public void say() { System.out.println("hi"); } }
|
test:
1 2 3 4 5 6 7 8 9 10 11
| package com.bayeeaa.demo1.d9_static;
public class test { public static void main(String[] args) { user u1 = new user("bb",123); user u2 = new user("ye",11); user.tea = "black tea"; System.out.println(u2.tea); System.out.println(u1.tea); } }
|
我们可以看到当我们给tea用static修饰了之后,在test文件中我们就可以直接用类来进行赋值而不需要用成员来赋值,并且赋值后每个成员所访问的值都是一样的。被static所修饰的成员变量叫做静态变量,它有这三个特点:
- 被该类所有对象共享
- 不属于对象,属于类
- 随着类的加载而加载,优先于对象的存在
当然被static修饰的成员方法,叫做静态方法,它有这些特点:
- 多用在测试类和工具类中
- Javabean类中很少会用到
这里我们就可以讨论到工具类的创建。
工具类创建
创建一个工具类,首先我们要将其私有化,这样外部就不会创建这个类的对象了(因为我们这个要让这个类作为工具,那我们就要写死在这里了,不能再让别人有所操作,不然工具变化会带来不便):
1 2 3
| public class ArrUtil { private arrUtil(){} }
|
我们写一个工具类的例子:
ArrUtil:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.bayeeaa.demo1.d9_static;
import java.util.ArrayList;
public class ArrUtil { private ArrUtil(){}; public static int get_sum(int[] arr){ int sum=0; for (int i = 0; i < arr.length; i++) { sum = sum + arr[i]; } return sum; }
public static int get_num_sum(ArrayList<user> list){ int sum = 0; for (int i = 0; i < list.size(); i++) { sum = sum + list.get(i).getNumber(); } return sum; } }
|
test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.bayeeaa.demo1.d9_static;
import java.util.ArrayList;
public class test { public static void main(String[] args) {
int[] a = {1,2,3,4,5,6}; System.out.println(ArrUtil.get_sum(a));
ArrayList<user> list = new ArrayList<>(); user u1 = new user("ye",1); user u2 = new user("yee",2); user u3 = new user("yeee",12);
list.add(u1); list.add(u2); list.add(u3);
System.out.println(ArrUtil.get_num_sum(list));
} }
|
可以看到可以直接通过 类名.方法名() 来调用工具。
this关键字
其实,static有这几个注意事项:
- 静态方法中,只能访问静态
- 非静态方法可以访问所有
- 静态方法中没有this关键字
而这几个关键都和this关键字有关。以下我来举一些例子。
test:
1 2 3 4 5 6 7 8 9 10 11
| package com.bayeeaa.demo1.d9_static;
import java.util.ArrayList;
public class test { public static void main(String[] args) { methods_class pj = new methods_class(); pj.repeat();
} }
|
methods_class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.bayeeaa.demo1.d9_static;
public class methods_class {
String s;
public void repeat(methods_class this){ System.out.println(this); }
public static void again(){ System.out.println(this); } }
|
我在methods_class文件中输出了两个方法,一个是非静态方法,一个是静态方法,当我们分别用test文件来打印的时候会发现第一个repeat方法打印出了对象的地址,而第二个again方法会报错。
在我们平时使用非静态方法应该是不会在非静态方法中手动调入methods_class this这个值的,但是我们却可以直接用方法调用,这是因为在非静态方法中是默认包括this的,而this的赋值又由jvm调取。所以说在非静态方法中如果我们要调用参数,比如num,实际上应该是这样的:
1 2 3
| public void repeat(methods_class this){ System.out.println(this.s); }
|
但是通过上图的报错我们可以看到静态方法中是没有this的。因此在其中要是强行引入this会导致报错。
有了这样的解释就可以理解上面的三个结论了。
内存解释
在作内存解释之前我们要知道:
栈内存(Stack Memory)
- 结构和管理方式:
- 栈内存按照先进后出的原则进行管理(LIFO,即 Last In, First Out)。
- 内存分配和回收由系统自动管理。每当一个函数被调用时,系统会在栈上为其分配内存,函数返回时,这部分内存会被自动释放。
- 用途:
- 用于存储局部变量、函数调用参数和返回地址等。
- 栈内存的管理非常高效,因为内存分配和释放的操作非常简单,只需调整栈顶指针即可。
- 特点:
- 内存分配速度快。
- 栈内存的大小通常较小,固定大小,超过限制会导致栈溢出(stack overflow)。
- 数据的生命周期由函数调用的生命周期决定,一旦函数返回,栈内存中的数据就会失效。
堆内存(Heap Memory)
- 结构和管理方式:
- 堆内存没有固定的管理顺序,分配和释放内存是由程序员或垃圾回收机制管理的。
- 内存的分配和释放可以发生在程序的任何地方,不像栈内存那样有固定的顺序。
- 用途:
- 用于动态分配内存,比如创建对象、数组等,需要程序员显式地请求内存空间。
- 适用于需要在多个函数调用间共享数据的情况。
- 特点:
- 内存分配和释放相对较慢,因为涉及到更复杂的管理机制。
- 堆内存的大小通常较大,由操作系统和系统配置决定。
- 需要程序员显式管理内存,未正确释放的内存会导致内存泄漏(memory leak),一些语言如 Java 和 Python 使用垃圾回收机制来自动处理这一问题。
总结
- 栈内存适用于存储生命周期短、大小固定的数据,管理简单且高效。
- 堆内存适用于存储生命周期长或大小不确定的数据,管理复杂但灵活。
我们可以简单的将内存划为三个区域:
字节文件加载:引入main文件,加载里面的方法、类文件中的属性、静态属性等等。
然后开始创建对象,在堆内存中开辟一块地,并返回其地址给对象pj。
然后pj就可以根据地址去寻找所要找的元素。
main的解释
现在我们再来看下我们每次的入口文件:
1
| public static void main(String[] args)
|
- public: 被JVM调用,访问权限足够大
- static:被JVM调用,不用创建对象,直接类名访问。因为main方法是静态的,所以测试类中其他方法也需要是静态的。(这里注意下,不是静态的叫实例变量,非static方法需要new一个实例出来才能使用,这里意思是main默认只能调用静态方法,实例要new)
- void:被JVM调用,不需要给JVM返回值
- main:一个通用的名称,虽然不是关键字,但是被JVM识别
- String[] args:以前用于接收键盘录入数据的,现在没有,被保留是为了上下版本兼容