目录
1、类变量(静态变量/静态属性)
内存布局
JDK7以上版本,静态域存储于定义类型的Class对象中,Class对象如同堆中其他对象一样,存在于GC堆中,之前在方法区
TODO:以后再看看?
定义
- 是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
- 什么时候需要用类变量? 当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, staticfee)
语法
- 访问修饰符 static 数据类型 变量名;
- static 访问修饰符 数据类型 变量名;
类变量与实例变量(普通属性)区别:
- 类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
- 类变量可以通过类名(子类名也ok).类变量名或者对象名.类变量名来访问,实例变量不能通过类名.类变量名方式访问。【前提是满足访问修饰符的访问权限和范围】
注意事项
- 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了就可以使用类变量了。
- 类变量的生命周期是随类的加载开始,随着类消亡而销毁
- 只加载一次
实践:第二个输出100,red,那个white只加载一次,被对象c修改后就永久修改了,c1和其共享
2、类方法 (静态方法)
使用场景
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。比如:工具类中的方法utils
类方法和普通方法区别联系
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区∶
- 类方法中无this的参数。普通方法中隐含着this的参数
- 类方法可以通过类名(子类名也ok)调用,也可以通过对象名调用。普通方法和对象有关,需要通过对象名调用,
- 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
- 类方法(静态方法)中只能访问静态变量或静态方法。普通成员方法,既可以访问非静态成员,也可以访问静态成员。
- 小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
3、main方法进一步理解
解释main方法的形式:public static void main(Stringl] args)
- main方法是虚拟机调用
- java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
- java虚拟机在执行main(方法时不必创建对象,所以该方法必须是static
- 该方法接收String类型的数组参数,该数组中保存 执行java命令(对应javac、java那个java命令)时传递给所运行的类的参数,
在dos下可以 java 执行的程序 参数1参数2参数3
idea下可以
package zhh.chapter09.main_;
public class TestMain {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
4、代码块
代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过0包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
语法
//修饰符可写可不写,写的话只能static
//最后的";"写不写都行
(修饰符){
代码
};
好处和使用场景
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的复用性
类什么时候被加载
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
注意事项
- static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。
- 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
- 如果只是使用类的静态成员(没创建对象)时,普通代码块并不会执行。
- 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
- 在类加载时,静态代码块和属性初始化就执行完毕,因此是优先于构造器和普通代码块执行的
- 构造器的最前面其实隐含了super()和调用普通代码块,
class A {
public A(){
//super(隐藏)
//普通代码块(隐藏)
//正常构造器代码
}
}
创建一个对象时,在一个类调用顺序
- 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
- 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
- 调用构造方法。
创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类的构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类的构造方法
package zhh.chapter09.codeblock;
/**
* 创建一个子类对象时,
* 他们的静态代码块,静态属性初始化,
* 普通代码块,普通属性初始化,构造方法
* 的调用顺序如下:
*/
import com.sun.xml.internal.bind.v2.TODO;
public class CodeBlockSequence {
public static void main(String[] args) {
//老师说明
//(1) 进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
//new B02();//对象
new B02();
}
}
class A02 { //父类
private static int n1 = getVal01();
static {
System.out.println("A02 的一个静态代码块..");//(2)
}
{
System.out.println("A02 的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器
//隐藏
//super()
//普通代码和普通属性的初始化......
System.out.println("A02 的构造器");//(7)
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("B02 的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02 的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化...
System.out.println("B02 的构造器");//(10)
}
}