目录
static 关键字
可以修饰 普通成员变量 和 普通成员方法 (补充:严格来说static修饰的变量和方法属于类,不属于对象,只是习惯性称呼为静态成员变量和方法)
特点
1.被类的所有对象所共享:(判定是否使用static的关键)
a.当static修饰了成员变量,该成员变量变量的值,就不在存储与对象中了,而是,单独存储了一份,被类的所有对象所共享
b.当static修饰成员方法的时候,该方法被当前类的所有对象共享
当前类对象.方法(和普通成员方法从共享的角度,几乎没有太大区别)
2.可以通过类名访问
a.可以通过类名直接访问,static成员变量的值
b.直接通过类名直接调用,static成员方法
3.随着类的加载而加载
a. static成员变量,随着类加载过程,其实就已经在方法区中,分配了内存
b. static成员方法,一旦类加载完毕,我们就可以直接访问,static方法,而不必等待,创建对象,然后在对象名访问方法
4.优先于对象而存在
1.不依赖于对象而存在
a.成员变量的角度来理解,static修饰的成员变量,不在依赖于对象而存在,因为static修饰的成员变量的值。不在存储在,该类的每个对象中
作为对比,没有被static修饰的成员变量,都依赖于对象而存在,因为他们的值,都存储在对象中
b.成员方法角度,被static修饰的成员方法,在没有对象存在的情况下,也可以直接通过类名来调用方法;作为对比,没有被static修饰的,普通的成员方法,它依赖于对象而存在,原因是,普通成员方法中,可以访问,普通成员变量的值,而普通对象的值又是依赖于对象而存在的
2.先出现在内存(时间在先,主要是从static成员变量和没有被static修饰的普通成员变量的对比) 静态成员变量,一定 优先与 没有被static修饰的普通成员变量
注意事项:
a.非静态成员变量(非静态的成员方法),不能在静态上下文(静态方法的方法体)中被访问 为什么???
1)之所以静态方法的方法体中,不能访问非静态成员变量,因为静态方法,优先于对象而存在 首先,我们可以在没有任何对象存在情况下,就可以直接通过类名,运行静态方法,所以静态方法,是访问不到当前对象的普通成员变量值(因为此时,当前对象不存在)
2)之所以静态方法方法体中,不能访问非静态的成员方法,因为,如果静态方法可以直接调用非静态的方法,非静态的方法可以去访问当前对象的成员变量值,而当前对象此时有可能还不存在
b.静态方法 or 非静态方法,方法体中都不能使用static 关键字定义变量
c.静态方法的使用场景 静态方法和非静态方法除了访问方式不同, 最大的区别,就是静态方法可以访问到的数据集合和非静态的方法不同(静态方法方法体中,不能访问非静态的成员变量)所以通常,静态方法它所访问的数据:要么是静态的成员变量,要么是方法的形式参数
通常,定义静态方法,都是为了方别使用该方法的功能(工具方法,例如.toString()),让别人使用该方法的功能, 为了方便使用该方法,通常,将这种工具方法定义为静态方法(访问的数据通常是方法参数),不用创建对象就可以使用
public class StaticDemo {
public static void main(String[] args) {
StaticClass staticClass = new StaticClass();
//1.被类的所有对象所共享
//静态成员变量的值,被类的所有对象共享
System.out.println(staticClass.staticField);//100
//静态成员方法,被类的所有对象共享
staticClass.testStatic();//testStatic
//2.可以通过类名访问
System.out.println(StaticClass.staticField);//100
StaticClass.testStatic();
//StaticClass.testMethod();错误,普通方法需要对象
}
}
class StaticClass {
//定义静态成员变量
static int staticField = 100;
double doubleValue = 9.9;
//定义静态成员方法
public static void testStatic() {
System.out.println("testStatic");
}
public void testMethod() {
System.out.println("testMethod");
}
}
static 单独存储示意图---共享
静态成员方法与普通成员方法图解
class StaticClass {
//定义静态成员变量
static int staticField = 100;
double doubleValue = 9.9;
//定义静态成员方法
public static void testStatic() {
System.out.println(staticField);
test();
}
public static void test() {
}
public void testMethod() {
//可以直接访问,静态成员方法和静态成员变量
System.out.println(staticField);
testStatic();
test();
}
}
静态成员变量和普通成员变量:
所属不同:静态变量属于类,所以也称为为类变量 成员变量属于对象,所以也称为实例变量(对象变量)
内存中的位置不同:静态变量存储于方法区的静态区 成员变量存储于堆内存
内存出现时间不同:静态变量随着类的加载而加载,随着类的消失而消失 成员变量随着对象的创建而存在,随着对象的消失而消失
调用不同:静态变量可以通过类名调用,也可以通过对象调用 成员变量只能通过对象名调用
静态上下文 不能访问非静态的成员变量或成员方法的补充
a.在静态方法中,不能访问的非静态的成员变量或成员方法(this的)
b.在静态方法中,对于非this的普通成员变量或成员方法可以访问
class StaticClass1 {
int i;
public static void test(StaticClass1 obj) {
//在新创建的对象上访问普通成员变量或成员方法不会有问题
StaticClass1 staticClass1 = new StaticClass1();
System.out.println(staticClass1.i);//正确
staticClass1.method();//正确
//不能this,当前对象不存在
this.i;//错误
this.method();//错误
//obj 是已经存在的对象
System.out.println(obj.i);//正确
obj.method();//正确
}
public void method() {
}
}
代码块
1.在Java中,使用{ }括起来的代码被称为代码块
2.根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块(多线程讲解)
局部代码块(开发中不会用):
1.声明位置:定义在方法体中的用括起来的一段代码
2.执行时机:随着方法的执行而执行
优点:限定变最生命周期,及早释放,提高内存利用率
(这个优点理论上确实存在,但是这个优点,在现在jvm中其效果,微乎其微的,甚至可以忽略不计)我们在开发中,同时还要追求,代码可维护性(包括代码的可读性) 在嵌套的代码块中,不能定义同名变量
public static void main(String[] args) {
testLocalBlock();
}
//局部代码块
public static void testLocalBlock() {
//局部代码块:啥时执行呢?随着方法执行而执行
{
int i = 0;
System.out.println(i);
}
}
public class Demo1 {
public static void main(String[] args) {
//可以在住方法上创建一个对象,然后调用方法
Demo1 demo1 = new Demo1();
demo1.testLocalBlock();
}
//局部代码块
//不加static
public void testLocalBlock() {
//局部代码块:啥时执行呢?随着方法执行而执行
{
int i = 0;
System.out.println(i);
}
}
}
构造代码块:
1.声明的位置,类中,方法体之外{ }包含的代码
2.执行特征:创建对象的时候执行,而且每次创建对象的时候必执行 构造方法和构造代码块,都是在创建对象的时候执行,有没有先后顺序呢?构造代码块先执行,构造方法后执行
使用场景:
1.可以把多个构造方法方法中相同的,提取朱来放到构造代码块中,每次调用构造都执行,并且在构造方法前执
2.我们也可以用构造代码块来初始化对象的成员变量值
关于成员变量初始化语句,构造代码块中的赋值语句,以及构造方法中的赋值(都是对成员变量的赋值),有如下结论:
1.不管是成员变量的初始化语句,还是构造代码块中的赋值语句,它们都先于构造方法的赋值执行
2.初始化语句和构造代码块中的赋值语句,执行的先后顺序和代码书写顺序有关,谁在前谁就先执行
public class Demo2 {
public static void main(String[] args) {
CodeBlockClass codeBlockClass = new CodeBlockClass();
}
}
class CodeBlockClass {
int i;
int j;
{
System.out.println("构造代码块1执行了");
}
public CodeBlockClass() {
System.out.println("构造方法执行了");
}
{
System.out.println("构造代码块2执行了");
}
}
/*
构造代码块1执行了
构造代码块2执行了
构造方法执行了
*/
public class Demo2 {
public static void main(String[] args) {
CodeBlockClass codeBlockClass = new CodeBlockClass();
CodeBlockClass codeBlockClass1 = new CodeBlockClass(1);
CodeBlockClass codeBlockClass2 = new CodeBlockClass(1, 2);
}
}
/*假设,我有这样一个功能要实现:我希望有一段代码,不管在创建对象时,用的是哪个构造方法,来初始化对象初值,我都希望这段代码一定执行*/
class CodeBlockClass {
int i;
int j;
{
System.out.println("构造代码块1执行了");
this.i = 10;//初始化
this.j = 20;//初始化
}
public CodeBlockClass() {
System.out.println("无参构造方法执行了" + i + j);
}
public CodeBlockClass(int i) {
this.i = i;
System.out.println("一参构造方法执行了" + i);
}
public CodeBlockClass(int i, int j) {
this.i = i;
this.j = j;
System.out.println("两参构造方法执行了" + i + j);
}
{
System.out.println("构造代码块2执行了");
}
}
/*
构造代码块1执行了
构造代码块2执行了
无参构造方法执行了1020
构造代码块1执行了
构造代码块2执行了
一参构造方法执行了1
构造代码块1执行了
构造代码块2执行了
两参构造方法执行了12
*/
静态代码块:
1.声明位置:类中方法体之外,除此之外,还被static关键字修饰 static { }
2.执行特征:静态代码块,随着类加载而执行,所以静态代码块至多执行一次
注意事项:静态上下文,多了个静态代码块
class CodeBlockClass{
//static int staticInt = 100; 先执行
static {
staticInt = 200;
System.out.println("静态代码块执行了");
}
//static int staticInt = 100; 后执行
}