静态变量的使用代码:cqh-静态变量
静态变量的使用代码:cqh-静态变量身高、体重、性别...这些状态,每个对象都不同,独一份的,作为实例变量。实例变量在 new 创建对象后调用 <init> 方法进行显示初始化,静态变量在类加载的初始化阶段时显示初始化。虽然可以通过引用.变量名访问,但是实际上还是通过类名调用,不用创建对象,与实例无关。(最好使用类名.访问,与实例变量区分开) class Test { static int i1 = 5; int i2 = 5; public static void main(String[] args) { Test t = null; int i1 = t.i1;// 会自动转成 Test.i1 int i2 = t.i2;// 空指针 } }每次创建对象都是在堆中开辟新的空间存储实例变量值,一个对象一份实例变量。
a. 面向对象-静态内容导视:
- 静态变量
- 静态方法
- 代码块
内容导视:
- 静态变量声明
- 什么时候使用静态变量
- final
静态的成员变量也称类变量,是该类的所有对象共享的变量。
语法:访问权限修饰符 static 数据类型 变量名;
虽然可以通过引用.变量名访问,但是实际上还是通过类名调用,不用创建对象,与实例无关。(最好使用类名.访问,与实例变量区分开)
class Test {
static int i1 = 5;
int i2 = 5;
public static void main(String[] args) {
Test t = null;
int i1 = t.i1;// 会自动转成 Test.i1
int i2 = t.i2;// 空指针
}
}
每次创建对象都是在堆中开辟新的空间存储实例变量值,一个对象一份实例变量。静态变量是独立于对象的,只存在一份,所有对象共享。
JDK7 以上版本,静态变量(原本在方法区)存储于定义类型的 Class 对象中,Class 对象存在于堆中。
实例变量在 new 创建对象后调用 <init> 方法进行显示初始化,静态变量在类加载的初始化阶段时显示初始化。
a.1.2 什么时候使用静态变量身高、体重、性别...这些状态,每个对象都不同,独一份的,作为实例变量。
当需要某个类的所有对象共享一个变量时,没有必要每个对象一个,使用类变量可以节省空间。
a.1.3 finalfinal:最终的、最后的、不可变更的。
可以修饰类、字段、方法和局部变量。
- 被修饰的类无法被继承:public final class 类名。一个类如果已经是 final 类了,没有必要再将方法修饰成 final 方法。(连继承都不行,何谈重写)
- 被修饰的方法无法被重写:public final void 方法名() {}。
- 被修饰的变量无法被修改(即无法被重新赋值):final int 变量名;。
- final 不能修饰构造器。
final 与 static 修饰符不分先后顺序。
不希望类被继承、方法被重写、修改变量的值,就可以使用 final 修饰,相当于绝育。
常量
final 修饰的变量称为常量,变量名全部大写,单词之间使用下划线 “_” 连接。
final 修饰的字段可以赋值的位置
final 修饰的字段没有默认值,必须手动赋值;
实例变量可以赋值的位置:
- 声明时显示初始化
- 实例代码块中
- 构造器中
静态变量可以赋值的位置:
- 声明时
- 静态代码块中
实际使用
final 往往与 static 往往搭配使用,定义时以字面量赋值的方式,使用类名.常量名访问不会触发初始化,用到此常量的地方直接从常量池中取出值。
实例变量一个对象一份,并不是一成不变,不需要 final。静态变量所有对象共享一份,如果静态变量的值不会变,使用 final 修饰,效率更高。
、-- 或 = 等扩展赋值运算符
class Test {
public int doSome(final int x) {
x;
return x 1;
}
}
x 本质是 x = (int)(x 1),修改了 x 保存的值,但 final 修饰的变量不可以修改,会报错:不能分配最终参数 x;return x 1 没错。
可以修改 final 变量指向的对象的状态
final char[] value = {'s' 'b' 'a'};// value -> 0x677327b6 {0:'s' 1:'b' 2:'a'}
value[2] = 'c';// value -> 0x677327b6 {0:'s' 1:'b' 2:'c'}
System.out.println(value);// sbc
有人就说肯定不能啊,final 不可变啊!我们说的 final 不可变,指的是被 final 修饰的 value 变量保存的值不可变。此 value[2] 修改的是数组对象中的值, value 保存的地址依然为这个数组对象,并没有变。
a.2 静态方法内容导视:
- 静态方法
- main 方法
也称类方法;
语法:访问权限修饰符 static 返回值类型 方法名(形参列表) {...}
通过类名.方法名(实参)调用。调用本类的方法,可以省略类名。
public static int add(int num1 int num2) {
return num1 num2;
}
public static void main(String[]args) {
add(2 5);
}
当方法不直接访问实例相关的(包括 this、super),可以设计成静态方法。(静态方法中不能直接访问实例相关的,必须先创建对象,然后通过引用.访问)
class person {
String name;
// 返回 name,与实例有关,只能设计为实例方法
public String getName() {
// 返回调用此方法的实例的 name
return name;
}
}
// 静态方法所需的数据由形参提供(或静态变量)
public static char isBoy(boolean sex) {
return sex ? '男' : '女';
}
实例方法之所以可以直接访问实例变量,这是因为调用实例方法必须得有对象,那么实例方法中的实例变量就是此对象的状态。
a.2.2 main 方法public static void main(String[] args) {}
public:运行时,虚拟机需要调用类的 main 方法,所以此方法必须是 public
static:不必创建对象也能够调用 main 方法
args:接收执行 java 命令时传递的实参
java 类名 参数1 参数2 ...
可以传递参数后遍历 args
for (int i = 0; i < args.length; i ) {
System.out.println(args[i]);
}
在 IDEA 中如何传递参数?先运行类后再修改运行时配置
选中刚刚运行的类填写方法参数即 Program arguments,点击 OK。
main 方法本质是一个类方法,可以直接访问本类的类方法和类变量,但是不能直接访问本类的实例成员,必须先创建实例,才能通过引用去访问。
a.3 代码块内容导视:
- 实例代码块
- 静态代码块
- 代码执行顺序
代码块又称为初始化块,属于类中的成员(即是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过 {} 包围起来。
但和方法不同,它没有方法名,没有返回值,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时、创建对象时隐式调用。
基本语法
[修饰符] {
java 语句...
};
1)修饰符可选,要写的话,也只能写 static。
2)代码块分为两类,使用 static 修饰的称静态代码块,没有 static 修饰的,称为实例代码块。
3)逻辑语句可以为任何逻辑语句。(输入、输出、方法调用、循环、判断等)
4); 可以省略。
a.3.1 实例代码块实例代码块,创建对象时调用。
实例代码块总是在构造方法执行之前执行。如果多个构造器中都有重复的语句,可以抽取到实例代码块中,提高复用性。
class Person {
int age;
String name;
public Person(int age) {
System.out.println("此人已疯");
System.out.println("送入医院治疗");
System.out.println("恢复良好,申请出院");
this.age = age;
}
public Person(String name) {
System.out.println("此人已疯");
System.out.println("送入医院治疗");
System.out.println("恢复良好,申请出院");
this.name = name;
}
}
把构造器重复的地方抽取放进实例语句块中,可以改成:
class Person {
int age;
String name;
{
System.out.println("此人已疯");
System.out.println("送入医院治疗");
System.out.println("恢复良好,申请出院");
}
public Person(int age) {
this.age = age;
}
public Person(String name) {
this.name = name;
}
}
a.3.2 静态代码块
有 static 修饰的代码块称为静态代码块,在类加载的初始化阶段执行,一般做初始化操作如加载资源、配置文件等。类只加载一次,所以静态代码块也只执行一次。
class A {
static {
age = 88;// 可以赋值,但不能前向访问
}
static int age;
static {
System.out.println(age);
}
}
class Test {
static A a = new A();
public static void main(String[] args) {}
}
调用 Test 类的 main 方法前,会先初始化 Test 类,静态变量 a 此时显示初始化,a = new A()①
new 前会初始化 A 类,执行静态代码块 age = 88,输出 88 后,紧接着调用 A 类的 <init>(),执行结束后回到 ① 处将地址赋给静态变量 a。
紧接着调用 main 方法。
a.3.3 代码执行顺序我知道大家可能会有些迷惑。现在将零零散散的信息罗列出来。
实例变量
- 创建对象时在堆中开辟空间,为所有实例变量分配空间(包括继承来的),实例变量默认初始化,随后调用 <init> 方法。
- <init> 的第一句默认是调用父类的 <init>(可以追溯到 Object 的 <init>),第二句按声明的先后顺序执行实例代码块中的语句及实例变量的显示初始化,再执行构造器中的代码。执行完后将对象的地址赋给引用变量。
class A {
int age = 6;// 显示初始化
{
System.out.println(age);
}
// 每个构造器都对应一个 <init> 方法
public A() {
// 调用 <init>() 方法的过程
// 1.调用父类无参构造(可以被this(...)、super(...)替代)
// 2.执行实例语句块,显示初始化实例变量,如 age = 6;
// 3.执行 A() 中的代码(如下)
age = 8;
System.out.println(age);
}
public static void main(String[] args) {
new A();
}
}
输出 6、8。
无参构造器第一句如果替换为 this(int),调用 <init>()时,第一句跳到本类的 <init>(int),由它负责【调用父类的 <init>()、实例代码块的执行、实例变量的显示初始化】【只会执行一次,所以 <init>()不再执行这些内容】,当此 <init>(int)执行完后回到 <init>()方法,执行无参构造器中的代码。
静态变量
- 准备阶段时,静态变量默认初始化,不同类型的初始值如下:
数据类型 |
初始值 |
byte |
(byte)0 |
short |
(short)0 |
int |
0 |
long |
0L |
float |
0.0f(正数 0) |
double |
0.0d(正数 0) |
boolean |
false |
char |
'\u0000' |
reference |
null |
如果被 final 修饰的静态变量,在声明时使用字面量的方式赋值,则在此时显示初始化。
- 初始化阶段时,按声明的先后顺序执行静态代码块中的语句及静态变量的显示初始化。
什么时候初始化:
- 创建对象时
- 访问静态变量时(被 final 修饰、已在编译时就把值放入常量池的类静态变量除外)
- 修改静态变量时
- 调用静态方法时
- 使用 java.lang.reflect 包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化。
- 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。(对于接口而言则不需要触发父接口的初始化,除非真正使用到父接口)
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main 方法的那个类),虚拟机会先初始化这个主类。
- 当使用 JDK7 新加入的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial 四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
- 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
同一个类只初始化一次(类只加载一次,所以静态变量也只显示初始化一次)。
class A {
static {
age = 3;
}
static int age = 8;
static {
System.out.println(age);
}
public static void main(String[] args) {}
}
调用 main 方法前,应初始化 A 类,按声明的先后顺序执行静态代码块与静态变量的显示初始化:age = 3,age = 8,输出 age 即 8。
初始化阶段结束后,执行 main 方法,但没有任何语句,程序结束。
总结:
创建一个子类对象时,调用顺序如下:
- 执行父类的静态代码块与静态变量的显示初始化(仅第一次时)
- 执行子类的静态代码块与静态变量的显示初始化(仅第一次时)
- 创建子类对象
- 执行父类的实例代码块与实例变量的显示初始化
- 执行父类的构造器
- 执行子类的实例代码块与实例变量的显示初始化
- 执行子类的构造器
其它情况
final 修饰的类变量在声明时使用字面量赋值的方式,会在准备阶段显示初始化,访问此变量就不会触发类的初始化。
class A {
static final int COUNT = 5;// 如果调用方法完成赋值、在静态代码块中赋值,访问此变量就会触发初始化
static {
System.out.println("静态代码块~");
}
}
class B {
public static void main(String[] args) {
int i = A.COUNT;// 访问 COUNT 不会执行 A 中的静态代码块
}
}
借助子类访问父类的静态变量时,不会触发子类初始化但是会触发父类初始化。
class A {
static int i = 5;
static {
System.out.println("A 静态代码块~");
}
}
class B extends A {
static {
System.out.println("B 静态代码块~");
}
}
class C {
public static void main(String[] args) {
int i = B.i;// 不会触发 B 类的初始化,因为 i 是 A 的
}
}
a.x 总结回顾
所有对象共享的变量设为静态变量,使用类名.变量名访问。
不直接访问实例变量的方法设为静态方法,使用类名.方法名(实参列表)调用。
构造器中重复的语句可以抽取放入实例代码块中,每创建一次对象,就在构造器方法执行之前执行一次。
把在 main 方法执行前的代码放入静态代码块中。
不想被继承、重写、修改,可以使用 final 修饰。
a.y 课后习题a.1 控制台上输出?
1)
class Test {
static int count = 9;
public void count() {
System.out.println("count=" (count ));
}
public static void main(String[] args) {
new Test().count();
new Test().count();
System.out.println(Test.count);
}
}
2)修改错误后...
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
id ;
return total;
}
public Person() {
total ;
id = total;
}
}
class Test {
public static void main(String[] args) {
System.out.println(Person.getTotalPerson());
Person p1 = new Person();
System.out.println(Person.getTotalPerson());
}
}
3)
class A {
{
age = 55;
flog = 3;
}
int age = 4;
int flog;
public A() {
age = 99;
}
{
System.out.println(age);
System.out.println(this.flog);
}
public static void main(String[] args) {
A a = new A();
System.out.println(a.age);
System.out.println(a.flog);
}
}
4)
class Test {
{
age = 434;
System.out.println("最上面的实例语句块执行了");
}
int age = 6;
public Test() {
System.out.println("构造方法被调用");
age = 1234;
}
{
System.out.println(age);
}
static {
System.out.println("最下面的静态语句块执行了");
Test t = new Test();
System.out.println(t.age);
}
static {
System.out.println("结束");
}
public static void main(String[] args) {}
}
5)如果 B 类中的 a 字段是实例变量,在 main 方法中创建 B 类型的对象呢?
class A {
B b = new B();
public A() {
System.out.println("A构造方法被调用");
}
}
class B {
static A a = new A();
public B() {
System.out.println("B构造方法被调用");
}
public static void main(String[] args) {}
}
6)
class B1 {
static {
System.out.println("B1类被加载");
}
public B1() {
System.out.println("B1无参构造被调用");
}
}
class B2 {
static {
System.out.println("B2类被加载");
}
}
class Person {
static {
System.out.println("Person类被加载");
}
int age = 4;
static String country = "中国";
public Person() {
System.out.println("Person构造方法被调用");
age = 5;
}
}
class Test {
static {
System.out.println("Test类被加载");
}
static B1 b1 = new B1();
static B2 b2 = null;
Person p;
public static void main(String[] args) {
Test test = new Test();
test.p = new Person();
System.out.println(test.p.age);
System.out.println(test.p.country);
test.b2 = new B2();
}
}
7)
class Car {
double price = 10;
static String color = "white";
@Override
public String toString() {
return price "\t" color;
}
public Car() {
this.price = 9;
this.color = "red";
}
public Car(double price) {
this.price = price;
}
public static void main(String[] args) {
Car c = new Car();
Car c1 = new Car(100);
System.out.println(c);
System.out.println(c1);
}
}
8)
class S {
public S() {
System.out.println("看我干什么,我只是凑个热闹");
}
}
class A extends S {
{
age = 66;
}
int age;
public A(int age) {
// 局部变量 age = 局部变量 age,与实例变量 age 无关
age = age;
}
public A() {
System.out.println("无聊的无参构造器");
age = 33;
}
}
class B extends A {
{
fole = 44;
}
int fole = 0;
public B() {
this(252);
}
public B(int fole) {
super(5);
System.out.println("B类的有参构造:" this.fole "=" this.age);
}
public static void main(String[] args) {
B b = new B();
System.out.println(b.fole);
System.out.println(b.age);
}
}
9)
class S {
static {
System.out.println("S类被加载:" A.a);
}
static int age = 3;
}
class A extends S {
static int a = 32;
static {
System.out.println("A类被加载:" S.age);
}
}
class Test {
public static void main(String[] args) {
new S();
System.out.println(S.age);
System.out.println(A.a);
}
}
10)
class S {
static {
System.out.println("S类被加载:" A.a);
}
static int age = 3;
}
class A extends S{
static int a = 32;
static {
System.out.println("A类被加载:" S.age);
}
}
class Test {
public static void main(String[] args) {
new A();
}
}
11)
class S {
static {
System.out.println("S类被加载");
}
}
class A extends S {
static int age = 33;
static {
System.out.println("A类被加载");
}
}
class B extends A {
static {
System.out.println("B类被加载");
}
static String name;
int flog;
}
class Test {
static {
System.out.println("Test类被加载");
}
public static void main(String[] args) {
int age = A.age;
new B();
}
}
12)
class A{
static {
age = show();
}
static int age;
public static int show(){
age = 33;
return age ;
}
public static void main(String[] args) {
System.out.println(A.age);
}
}
a.z 习题答案
a.1 控制台上输出?
1)
class Test {
static int count = 9;
public void count() {
System.out.println("count=" (count ));
}
public static void main(String[] args) {
new Test().count();
new Test().count();
System.out.println(Test.count);
}
}
显示初始化 count = 9;
调用构造器,输出 count=9 后 count = 10
调用构造器,输出 count=10 后 count = 11
输出 Test.count,即 11
2)修改错误后...
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
id ;
return total;
}
public Person() {
total ;
id = total;
}
}
class Test {
public static void main(String[] args) {
System.out.println(Person.getTotalPerson());
Person p1 = new Person();
System.out.println(Person.getTotalPerson());
}
}
getTotalPerson 是静态方法,不能直接访问实例变量,将 id 去掉。
显示初始化 total = 0
调用 getTotalPerson 方法返回 total,输出 0
调用构造器 total = 1,id = 1
调用 getTotalPerson 方法返回 total,输出 1
3)
class A {
{
age = 55;
flog = 3;
}
int age = 4;
int flog;
public A() {
age = 99;
}
{
System.out.println(age);
System.out.println(this.flog);
}
public static void main(String[] args) {
A a = new A();
System.out.println(a.age);
System.out.println(a.flog);
}
}
执行实例代码块 age = 55,flog = 3
显示初始化 age = 4
执行实例代码块,输出 4,输出 3
回到构造器 age = 99
回到 main 方法,输出 99、3
注意实例语句块在构造器之前执行。
4)
class Test {
{
age = 434;
System.out.println("最上面的实例语句块执行了");
}
int age = 6;
public Test() {
System.out.println("构造方法被调用");
age = 1234;
}
{
System.out.println(age);
}
static {
System.out.println("最下面的静态语句块执行了");
Test t = new Test();
System.out.println(t.age);
}
static {
System.out.println("结束");
}
public static void main(String[] args) {}
}
执行静态代码块,输出 最下面的静态语句块执行了 ①
调用 <init> 方法
执行实例代码块,age = 434,输出 最上面的实例语句块执行了
显式初始化实例变量,age = 6
执行实例代码块,输出 6
执行无参构造,输出 构造方法被调用,age = 1234
回到 ①,输出 t.age 即 1234
执行静态代码块,输出 结束
5)如果 B 类中的 a 字段是实例变量,在 main 方法中创建 B 类型的对象呢?
class A {
B b = new B();
public A() {
System.out.println("A构造方法被调用");
}
}
class B {
static A a = new A();
public B() {
System.out.println("B构造方法被调用");
}
public static void main(String[] args) {}
}
显示初始化静态变量 a = new A()
调用 A 类的 <init>
- 显示初始化实例变量 b = new B()
- 调用 B 类的 <init>,输出 B构造方法被调用,执行结束后将地址赋给 b
- 执行 A 类的无参构造,输出 A构造方法被调用,执行结束后将地址赋给 a
B 类初始化结束,执行 main 方法。
如果 B 类中的 a 字段是实例变量,在 main 方法中创建 B 类型的对象呢?
调用 B 类的 <init>
- 显示初始化实例变量 a = new A()
- 调用 A 类的 <init>
- 显示初始化实例变量 b = new B()
- 调用 B 类的 <init>
- 显示初始化实例变量 a = new A()
- 调用 A 类的 <init>
...
循环调用,直到栈溢出...这种差异的出现是由于静态变量只显示初始化一次,而实例变量每次创建对象时都会显示初始化。
6)
class B1 {
static {
System.out.println("B1类被加载");
}
public B1() {
System.out.println("B1无参构造被调用");
}
}
class B2 {
static {
System.out.println("B2类被加载");
}
}
class Person {
static {
System.out.println("Person类被加载");
}
int age = 4;
static String country = "中国";
public Person() {
System.out.println("Person构造方法被调用");
age = 5;
}
}
class Test {
static {
System.out.println("Test类被加载");
}
static B1 b1 = new B1();
static B2 b2 = null;
Person p;
public static void main(String[] args) {
Test test = new Test();
test.p = new Person();
System.out.println(test.p.age);
System.out.println(test.p.country);
test.b2 = new B2();
}
}
初始化 Test 类
- 执行静态代码块输出 Test类被加载
- 显示初始化静态变量 b1 = new B1()【初始化 B1 类,执行静态代码块输出 B1类被加载,调用 B1 类的 <init>,输出 B1无参构造被调用,执行结束后,将地址赋给 b1】
- 显示初始化静态变量 b2 = null
调用 main 方法
- 调用 Test 类的无参构造,执行结束后,将地址赋给 test
- test.p = new Person()【初始化 Person 类,执行静态代码块输出 Person类被加载,显式初始化静态变量 country = "中国",调用 Person 类的无参构造,显示初始化实例变量 age = 4,输出 Person构造方法被调用,age = 5,执行结束后,将地址赋给 test.p】
- 输出 5、中国
- test.b2 = new B2()【初始化 B2 类,执行静态代码块输出 B2类被加载,调用 B2 类的无参构造,执行结束后,将地址赋给 test.b2】
- 程序结束。
7)
class Car {
double price = 10;
static String color = "white";
@Override
public String toString() {
return price "\t" color;
}
public Car() {
this.price = 9;
this.color = "red";
}
public Car(double price) {
this.price = price;
}
public static void main(String[] args) {
Car c = new Car();
Car c1 = new Car(100);
System.out.println(c);
System.out.println(c1);
}
}
初始化 Car 类
- 显示初始化静态变量 color = "white"
执行 main 方法
- 调用 Car 类的 <init>(),显示初始化实例变量 price = 10.0,price = 9.0,color = "red",执行结束将地址赋给 c。
- 调用 Car 类的 <init>(int),显示初始化实例变量 price = 10.0,price = 100.0,执行结束后将地址赋给 c1。
- 输出 c,调用 c.toString 方法,输出 9.0 red
- 输出 c1,调用 c1.toString 方法,输出 100.0 red
需要注意 color 是所有对象共享的变量。
上面只是故意为难人的题目,分个高低分化人群而已,使用类名.变量名访问,就不会那么容易混淆,不用对自己失去信心,第一名只有一个。
8)
class S {
public S() {
System.out.println("看我干什么,我只是凑个热闹");
}
}
class A extends S {
{
age = 66;
}
int age;
public A(int age) {
// 局部变量 age = 局部变量 age,与实例变量 age 无关
age = age;
}
public A() {
System.out.println("无聊的无参构造器");
age = 33;
}
}
class B extends A {
{
fole = 44;
}
int fole = 0;
public B() {
this(252);
}
public B(int fole) {
super(5);
System.out.println("B类的有参构造:" this.fole "=" this.age);
}
public static void main(String[] args) {
B b = new B();
System.out.println(b.fole);
System.out.println(b.age);
}
}
调用 B 类的 <init>()
- 调用 B 类的 <init>(int)
- 调用父类 A 的 <init>(int)
- 调用 S 的 <init>(),输出 看我干什么,我只是凑个热闹
- 执行实例代码块 age = 66,接着执行 A(int)中的 age = age 语句
- 执行实例代码块 fole = 44,显示初始化 fole = 0,输出 B类的有参构造:0=66
回到 main 方法,输出 0、66。
9)
class S {
static {
System.out.println("S类被加载:" A.a);
}
static int age = 3;
}
class A extends S {
static int a = 32;
static {
System.out.println("A类被加载:" S.age);
}
}
class Test {
public static void main(String[] args) {
new S();
System.out.println(S.age);
System.out.println(A.a);
}
}
new S()创建对象前
初始化 S 类,执行静态代码块,访问 A.a 前
【
初始化 A 类,显示初始化静态变量 A.a = 32,执行静态代码块,此时 S.age 还未显示初始化,
默认值 0,输出 “A类被加载:0”
】
输出 “S类被加载:32”,显示初始化静态变量 S.age = 3
创建对象,输出 3、32。
10)
class S {
static {
System.out.println("S类被加载:" A.a);
}
static int age = 3;
}
class A extends S{
static int a = 32;
static {
System.out.println("A类被加载:" S.age);
}
}
class Test {
public static void main(String[] args) {
new A();
}
}
new A()前
初始化 A 类
【
先初始化父类 S,执行静态代码块,此时 A.age 还未显示初始化,默认值 0,
输出 “S类被加载:0”,S.age = 3
】
A.a = 32,输出 “A类被加载:3”
11)
class S {
static {
System.out.println("S类被加载");
}
}
class A extends S {
static int age = 33;
static {
System.out.println("A类被加载");
}
}
class B extends A {
static {
System.out.println("B类被加载");
}
static String name;
int flog;
}
class Test {
static {
System.out.println("Test类被加载");
}
public static void main(String[] args) {
int age = A.age;
new B();
}
}
调用 main 方法前
初始化 Test 类,执行静态代码块,输出 “Test类被加载”
调用 main 方法,访问 A.age 前
初始化 A 类
【
初始化 S 类,输出 “S类被加载”
】
age = 33,输出 “A类被加载”
new B()前,初始化 B 类,由于类只初始化一次,不用再初始化父类,输出 B 类被加载
12)
class A {
static {
age = show();
}
static int age;
public static int show() {
age = 33;
return age ;
}
public static void main(String[] args) {
System.out.println(A.age);
}
}
初始化 A 类
- 执行静态代码块 age = show()
- 调用 show 方法,age = 33,返回 33 后自加一 age = 34;
- show 方法返回 33,赋给了 age,age = 33
调用 main 方法,输出 33