Java: static,final,代码块 的详解
每博一文案
山本文绪说过这样一句话:哪些决定放弃了的事,就请放弃得干干净净。哪些决定再也不见面的人,就真
的不要再见面了,不要再做背叛自己的事,如果想要爱别人,就先好好爱自己。
人生是一场有去无回的旅行,旧日种种,结成过往,我们只能回过头看,却不能从头走,好的也罢,坏的也罢,
过去的都已成定局了。不管是遗憾还是庆幸,都要带着这份烙印,继续走向漫漫人生。
正如那句极有名的禅语:无论你遇见谁,他都是对的人,无论发生什么事,那都是唯一会发生的事。
不管事情开始于那个时刻都是对的,时刻已经结束的,就是结束了。如果没有失去,就不会懂得珍惜。
但是你知道吗?如果已经失去了,就该学会放弃,没有什么是注定会属于我们的,得到或者失去,都是自有它的安排。
面对哪些注定要说再见到人或物,与其拼命拉扯,泪影婆娑,倒不如落落大方道一句“珍重”。
从从容容德告个别,然后留下一个潇洒离开的背影,从今以后更加坚定自己的路,将自己的人生过得有声有色。
愿你可饮最烈的酒,也能放开该放的手,从前不回头,往后强求。—————— 一禅心灵庙语
文章目录
- Java: static,final,代码块 的详解
- 每博一文案
- 1. static 关键字
- 1.1 static 修饰属性
- 1.2 static 修饰方法
- 1.3 static 修饰代码块
- 1.4 static 修饰内部类
- 1.5 开发中如何合理的使用 staitc 关键字
- 2. main 方法的探究
- 3. 代码块
- 3.1 程序中成员变量赋值的执行顺序
- 4. final 关键字
- 5. 总结:
- 6. 最后:
1. static 关键字
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。这里我们就可以用到 static
关键字的使用了。
static
: 表示的含义是 :静态的 。
在Java类当中, static 可以修饰属性,方法,代码块,内部类 。
1.1 static 修饰属性
static
修饰属性(变量): 按是否使用 static 修饰,可以分为为 静态属性(类属性) VS 非静态属性(实例属性) 。
非静态属性(实例属性): 我们创建了类的多个对象,每个对象都独立的拥有一套 类中的 非静态属性,非静态变量对应不同的实例对象(new) 自身的修改,不会影响到其他的 对象当中的一套非静态属性。
静态属性(类属性): 我们创建了类的多个对象,但是多个对象共享同一个静态属性,当通过某一个对象修改静态属性时,会导致其他对象调用此静态变量时,是最后一次修改过的结果。
被 static 修饰的属性的特点:
- 静态属性随着类的加载而加载,早于 new 对象的加载创建,因为 new 对象,要先加载类,再调用类中的构造方法创建对应的对象。
- 在访问权限允许的情况下,静态属性可以通过**“类名.静态属性名”** 的方式进行调用。
- 由于类只会加载一次,则静态属性(静态变量) 在内存(方法区)当中也只会存在一份,存在方法区的静态域当中。
- 静态属性有:System.out; Math.PI;
package blogs.blog2;public class StaticTest {public static void main(String[] args) {Chinese c1 = new Chinese();c1.nation = "CHN";Chinese c2 = new Chinese();c2.nation = "CHINA";System.out.println("c2.nation: "+c1.nation);System.out.println("c2.nation: "+c2.nation);// 可以通过 类名.静态属性 ()Chinese.nation = "中国";System.out.println("Chinese.nation: "+Chinese.nation);}
}class Chinese{String name;int age;static String nation; // 被 static 修饰的静态属性}
1.2 static 修饰方法
被 staitc 修饰的方法,被称为是 ”静态方法“ 或者是 类方法。
静态方法的特点:
- 同样静态方法也是和随着类的加载而加载到内存(方法区)当中的。
- 在访问权限允许的情况下,静态属性可以通过**“类名.静态方法名”** 的方式进行调用。
- 静态方法只能调用 静态的方法/属性 ,无法调用非静态的方法/属性,除非 实例化对象(new)。因为一个(静态方法)是已经在内存当中存在了的,调用一个(非静态的)还没有加载到内存当中的,JVM 是不允许的。
- 在静态的方法内:不能使用 'this’关键字 以及 super 关键字,他俩都是表示对象的引用.
- 非静态方法可以调用非静态方法或属性,也可以调用静态方法或属性。
- 被 static 修饰的方法无法重写
package blogs.blog2;public class StaticTest {public static void main(String[] args) {// 静态方法可以直接使用 “类名.静态方法”调用Chinese.show();}
}class Chinese{String name;int age;static String nation; // 被 static 修饰的静态属性public static void show(){System.out.println("我是静态方法");// name = "TOM"; // 静态方法无法直接调用非静态的属性// eat(); // 静态方法无法直接调用非静态的方法,可以通过 new 实例对象,调用/*this.name = "Tom";super.clone();*/ // 静态方法中无法使用 this./super.// 静态方法可以直接调用静态属性,静态方法nation = "Hello"; // 静态属性sleep(); // 静态方法}public void eat(){System.out.println("吃饭");System.out.println("***************************");// 非静态方法可以直接调用 静态方法/属性,因为加载内存的先后原因nation = "World"; // 静态属性sleep(); // 静态方法}public static void sleep() {System.out.println("睡觉");}
}
被 stiatic 修饰的方法,继承时,无法被重写,就算你按照重写的要求,编写好了没有报错,但是运行的时候,调用的是父类中没有被重写的方法 因为 stiatic 是类方法,和类一起加载到内存当中的,而多态中的重写是,运行时类型,只有运行了。才
package blogs.blog2;public class StaticTest {public static void main(String[] args) {Earth earth = new Chinese(); // 多态earth.human();}}class Chinese extends Earth{public static void human() {System.out.println("中国人");}
}class Earth{public static void human(){System.out.println("人类");}}
1.3 static 修饰代码块
被 static 修饰的代码块,被称为 “静态代码块”。
具体详细,继续往下看,在 3.代码块 这一段有详细说明
1.4 static 修饰内部类
关于这一部分内容,大家可以移步至:🔜🔜🔜
1.5 开发中如何合理的使用 staitc 关键字
开发中:如何确定一个属性是否声明未 static ???
如果一个属性可以被多个对象共享的,不会随着对象不同而不同的,就可以声明为 staitc
一般 static 是和 final 一起修饰 变量的。
开发中:如何确定一个方法是否要声明为 static 的 ???
一般是 stiatic 的属性,通常对应的 set/get 方法也是 stiatic 的
工具类中的方法,习惯上声明为 static 的,比如:Math,Arras,Collections
2. main 方法的探究
public
权限修饰符为什么是 public ???
由于Java虚拟机需要调用类的 main() 方法,所以该方法的访问权限 必须 是
public
才行。
- 为什么 main 方法的修饰符要是
static
静态的 ???
因为Java虚拟机在执行 main() 方法时,不必创建对象,所以该方法必须是
static
,可以直接调用。又因为 main 方法是 static 静态的,静态方法无法直接调用非静态的方法/属性,
必须创建对应类的实例对象(new)后,才能通过这个对象去访问该对应类中的非静态方法/属性。
- main 方法中的形参列表是
string[] args
字符串数组
该形参接收一个 String 类型的数组参数,该字符串数组中保存执行 java 命令时,所传递给所运行类的参数。
main 也是一个方法,特殊之处就是,它是一个程序入口的方法。不同的类中的是可以相互调用 main 的方法的,如下
因为有两个类,每个类中都有一个 main() 方法,所以执行的时候,你需要选择,想要运行的是哪一个告知编译器。
package blogs.blog2;public class MainTest {public static void main(String[] args) {System.out.println("我是 MainTest 中的 main()方法");}
}class Main2{public static void main(String[] args) {System.out.println("我要调用 MainTest中的main()方法");// 调用 MainTest 中的main()方法MainTest.main(args);}
}
3. 代码块
代码块(或初始化块)的作用: 对Java类或者对象进行初始化
代码块的分类:
- 一个类中代码块中若有修饰符,则只能被
static
修饰,被 static 修饰的代码块被称为 静态代码块。 - 没有任何修饰符,修饰的代码块,被称为非静态代码块
静态代码块的特点:
- 可以有输出语句
- 可以对类的属性,类的声明进行初始化操作
- 不可以对非静态的属性初始化,无法直接调用非静态的的方法/属性,想要 实例化对象 new
- 静态代码块随着类的加载而加载,并执行,且执行一次。所以静态代码块的执行要先于非静态代码块
- 一个类中可以有多个静态代码块,若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态的代码块的诸多限制和 “staitc" 是一样的。
非静态代码块的特点:
- 和静态代码块一样可以有输出语句
- 可以对类的数,类的声明进行初始化操作。
- 不仅可以调用静态的方法/属性,也可以调用非静态的方法 / 属性
- 一个类中可以有多个非静态代码块,若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 注意的是非静态代码块是,创建对象 (new) 的时候,才会执行,而且每创建一次对象都会执行一次,并且先于构造器执行。
无论是静态代码块/还是非静态代码块,都是加载后并执行的,并不是加载到内存当中不执行的。
注意: 非静态代码块,不是创建对象 (new) 是不会加载调用执行非静态代码块的
package blogs.blog2;public class BlockTest {public static void main(String[] args) {Person.show();}
}class Person{String name;static String desc;public void eat() {System.out.println("非静态方法");}public static void show(){System.out.println("静态方法");}{System.out.println("非静态代码块");}static{System.out.println("静态代码块");// name = "Tom"; // 静态代码块无法直接调用非静态的// eat();}
}
静态代码块优先比非静态代码块优先被执行,并且静态代码块仅仅只是执行一次(加载类的那一次)
package blogs.blog2;public class BlockTest {public static void main(String[] args) {Person person = new Person();Person person2 = new Person();}
}class Person{String name;static String desc;public void eat() {System.out.println("非静态方法");}public static void show(){System.out.println("静态方法");}{System.out.println("非静态代码块");}static{System.out.println("静态代码块");// name = "Tom"; // 静态代码块无法直接调用非静态的// eat();}
}
练习: 观察如下代码的运行结果:
先执行父类,再执行静态代码块
package day15;public class LeafTest {public static void main(String[] args) {new Leaf();System.out.println("*************************");new Leaf();}
}class Root{static {System.out.println("Root的静态初始化块");}{System.out.println("Root的普通初始化块");}public Root(){super();System.out.println("Root的无参数的构造器");}}class Mid extends Root{static{System.out.println("Mid的静态初始化块");}{System.out.println("Mid的普通初始化块");}public Mid() {super();System.out.println("Mid的无参数的构造器");}public Mid(String msg) {// 通过this调用一类中的重载的构造器this();System.out.println("Mid的带参数构造器,其参数数值: "+msg);}
}class Leaf extends Mid{static{System.out.println("Leaf的静态初始化块");}{System.out.println("Leaf的普通初始化块");}public Leaf() {// 通过super调用父类中有一个字符串参数的构造器super("尚硅谷");System.out.println("Leaf的构造器");}
}
package day15;public class Son extends Father {static {System.out.println("44444444444444444");}{System.out.println("55555555555555");}public Son() {System.out.println("66666666666666");}public static void main(String[] args) {System.out.println("77777777777");System.out.println("**********************************");new Son();System.out.println("*********************************");/*new Son();System.out.println("*************************************");new Father();*/}
}class Father{static{System.out.println("11111111111");}{System.out.println("222222222222");}public Father() {System.out.println("33333333333333");}
}
3.1 程序中成员变量赋值的执行顺序
如下是对属性赋值的先后所执行的顺序
- 默认初始化
- 显式初始化
- 在代码块中赋值
- 构造器中初始化
- 有了对象以后,可以通过 ”对象.属性,或对象.方法“ 的方式,进行赋值。
public class OrderTest {public static void main(String[] args) {Order order = new Order();System.out.println(order.orderId);}
}class Order{{orderId = 4;}int orderId = 3;public Order() {this.orderId = 5;}}
把构造器初始化注释掉,结果是
4. final 关键字
final
: 最终的含义。
final
可以用来修饰:类,方法,变量,局部变量(形参)。
final
修饰一个类,此类就不能被其他类所继承了
比如:System 类,String 类,StringBuffer类:因为里面的方法基本都实现了,没有必要再通过继承扩展了。
final 修饰方法:表明此方法不可以被重写,比如:Object 类中的 getClass()
final 修饰属性:可以考虑赋值的位置有:显示初始化,代码块初始化,构造器,注意是:赋值不是修改,被 final 修饰的变量无法被修改了。
显示初始化
package blogs.blog2;public class FinalTest {final int num = 10;public static void main(String[] args) {FinalTest f = new FinalTest();System.out.println(f.num);}
}
代码块初始化:
package blogs.blog2;public class FinalTest {final int LEFT;{LEFT = 100;}public static void main(String[] args) {FinalTest f = new FinalTest();System.out.println(f.LEFT);}
}
构造器初始化:
package blogs.blog2;public class FinalTest {final int num;public FinalTest(int n) {num = n;}public static void main(String[] args) {FinalTest f = new FinalTest(1000);System.out.println(f.num);}
}
final 修饰局部变量(形参) 表明此形参时一个常量,当我们调用此方法时,给常量形参赋值一实参,一旦赋值以后,就只能在方法体中使用此形参,并且不能再进行重新的赋值操作了。
package blogs.blog2;public class FinalTest {final int num;public FinalTest(final int n) {num = n;}public static void main(String[] args) {FinalTest f = new FinalTest(1000);System.out.println(f.num);}
}
一般是 static, final
用来修饰属性,全局常量,需要注意的是一般 static 的成员属性,方法也是 static 的
练习:
观察如下代码是否会出现报错,如果不会报错,运行的结果又是什么???
package blogs.blog2;public class FinalTest {public static void main(String[] args) {AA aa = new AA();aa.test(aa);}}class AA {String name;int age ;public void test(final AA aa) {aa.age = 10; // 这里是否或报错,能否编译成功System.out.println(aa.age);}
}
解析:
答:不会报错,结果是 10 。
因为
final
修饰的是 AA 这个引用类型的形参,并不会作用于 AA 类中的属性。如果我修改 AA 这个引用类型的地址,可能就会报错了。
5. 总结:
- static 修饰属性,方法,代码块,内部类的不同的作用,
- static 静态的随着类一起加载到内存(方法区)当中(仅仅加载一次,所有对象共有),早于 new 对象的加载创建,因为 new 对象,要先加载类,再调用类中的构造方法创建对应的对象。
- static 静态的属性,方法,代码块 都无法直接的访问非静态的方法/属性,需要通过 创建实例对象(new)的方式访问
- 被 static 修饰的方法,无法被重写,就算编写成了重写的方式了,但是调用的时候,执行的不是子类重写的方法,而是父类中没有被重写的方法。
- main 方法的 public , static ,String[] args 的作用。
- 代码块:静态代码块,非静态代码块。
- 静态代码块:随着类一起加载到内存(方法区)当中,加载的同时,并执行代码块,并且(无论加载多少次类)只执行一次(加载类的那一次)。优先于非静态代码块,优先于构造器。
- 非静态代码块:只有当创建实例对象(new)的时候才会被加载并执行,创建多少次对象就会执行多少次非静态代码块。
- final 最终的,可以修饰:属性(不可修改),形参(不可修改),方法(无法重写),类(无法继承)。
6. 最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见!!!