目录
一.static关键字是什么
1.static修饰的代码属于类
2.定义的变量存储在方法区的静态常量池中
二、java为什么设计static关键字
方便类直接去使用自己的方法和变量
1.假设方法和变量上没有static关键字
(1)如果方法和变量是非静态的,他们只有在对象当中才会被分配内存空间,也就是只有对象才能调用
(2)A类中的这些代码只是以信息的形式存储在方法区当中,并没有独立的存储空间,
当main方法入栈,执行A a=new A(); 的时候,才在堆内存里有了存储空间
A.java
public class A {
public String name="admin";
public void run(){
}
public void flay(){
}
}
Test.java
public class Test {
public static void main(String[] args) {
A a=new A();
a.run();
a.flay();
a.name="aa";
}
}
(3)堆栈内存图:
2.假设方法和变量上有static关键字
(1)方法和变量能直接被类调用,因为它们在方法区中已经有了自己独立的存储空间
(2)从上个知识点可以看出:如果没有static关键字,那么类是不可以调用自己的属性和方法的,只能用对象去调用
(3)此时A中的信息直接存储在方法区中的静态常量池中,被所有对象共享,拥有独立的存储空间
A.java
public class A {
public static String name="admin";
public static void run(){
}
public static void flay(){
}
}
Test.java
(4)
A a=new A();
a.run();
a.flay();
a.name="aa";
这些代码没有报错,是因为对象来源于类,那么对象可以使用类的信息
但是推荐使用:
A.name="b";
A.run();
A.flay();
(5)堆栈内存图:
三、static关键字怎么用
static可以修饰变量、方法和代码块
1.修饰变量
stati修饰的变量属于类变量,存储在方法区的静态常量池中,被所有对象共享
比如:
public class A {
public static String name="admin";
public String address="张三";
public void run(int num){
System.out.println("跑了"+num+"米");
}
@Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
"address='" + address + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
A a1=new A();
A a2=new A();
a1.name="李四";
a1.address="北京";
a1.run(10);
a2.name="王五";
a2.address="上海";
a2.run(20);
System.out.println(a1.toString());
System.out.println(a2.toString());
}
}
输出:
堆栈内存图:
static修饰的变量被所有类共享
方法谁调用就是谁的,方法是使用;而这个变量相当于内存空间,共享的是一块空间
2.修饰方法
(1)static修饰的方法无法调用到非静态方法
如果一个方法想要被main方法/静态方法调用,两种方法:
1 | 在非静态方法上加上static |
2 | 在main方法中创建对象,让对象调用该非静态方法 |
(2)非静态方法可以调用静态方法
比如:
原因:
flay()想要表达的时候就已经创建好了对象,有对象就一定有类(类是创建对象的模板),那么静态方法一定是存在的
3.修饰代码块
在main方法之前运行,目的是优化程序运行
例如:
public class Test extends Base{
public Test(){
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test();
}
static {
System.out.println("test static");
}
}
class Base{
static {
System.out.println("base static");
}
public Base(){
System.out.println("base constructor");
}
}
运行结果:
这样输出的原因:
执行过程:扫描----加载/编译----运行
程序执行先去找main()方法启动程序
1.扫描Test类,发现有父类Base类,先去扫描父类,发现Base类没有父类
2.将Base.class加载进方法区,此时Base类中的static代码块执行,所以先输出base static
3.将Test.class加载进方法区,此时Test类中的static代码块执行, 所以然后输出 test static
4,main方法入栈,main执行new Test();去创建Test类的对象
5.创建子类对象之前先创建父类对象,所以先执行Base()构造器,再执行Test()构造器,所以最后输出 base constructor和test constructor
四、和static有关的三道题
1.
public class Test {
private static int a;
public static void main(String[] args) {
modify(a);
System.out.println(a);
}
public static void modify(int a){//这个是局部变量(只在方法内部起作用)
a++;
}
}
输出:
原因:
堆栈内存图:
下一步是modify()这个方法出栈 ,出完栈之后输出的是全局变量a的值,即0
2.
public class Test {
private int a;
public static void main(String[] args) {
Test a1=new Test();
modify(a1.a);
System.out.println(a1.a);
}
public static void modify(int a){
a++;
}
}
输出:
原因:
堆栈内存图:
下一步是modify()这个方法出栈 ,出完栈之后输出的是全局变量a的值,即0
3.
public class Demo {
public Demo(String aa){
System.out.println("==="+aa);
}
static {
System.out.println("11");
}
public static Demo demo=new Demo("+++");
static {
System.out.println("22");
}
}
class Test1{
public static void main(String[] args) {
Demo demo=new Demo("----");
}
}
运行结果:
执行过程:扫描----加载/编译----运行
(1).main方法在哪里,就先扫描哪个类,先扫描Test类,发现没有父类,也没有static修饰的代码,那么编译Test类,并且main方法入栈
(2).执行Demo demo=new Demo("----");之前扫描Demo类,发现没有父类,所以编译Demo类,在编译的过程中,static代码块按顺序优先执行
(3).首先输出 11
(4).执行 public static Demo demo=new Demo("+++"); 此时涉及到调用Demo构造方法,输出 ===+++
(5).输出 22
(6).编译完毕,回到main方法中继续执行 new Demo("----"); 现在可以创建对象了,也就是进行到了运行阶段
(7).通过调用构造器创建对象:===----
(8).输出===----