🌹🌹前言:此篇是出自JAVA中的类和对象【一】的下篇,此篇主要以static,代码块,内部类等知识为主。
目录
一、static
1.1 static修饰成员变量
static修饰的成员变量,称为 静态成员变量,也可以称之为 类成员(可以不用实例化对象,直接通过 类名.变量名 来访问),静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
🚩🚩举个例子:
我们知道,当实例化一个对象后,每个对象都会存储这些成员变量,可是当两者都有同一属性(班级是一样的)的时候,如果再把这个两者都有的属性(班级)各自保留一份就没有必要了,所以这时static派上了用场,在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
因此,上述代码优化后如下:
public class Student {
public String name;
public int score;
public int age;
public static final int classes = 3;//使用final修饰,变量无法改变,更加安全。
public Student(String name, int score, int age) {
this.name = name;
this.score = score;
this.age = age;
}
}
🎈🎈总结:
静态成员变量的特性:
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁
5.在方法的内部定义的变量叫做局部变量,是存储在栈区上的,局部变量出了方法(方法的作用域)就会被栈回收,而static修饰的变量存储在方法区上,所以static修饰的变量不能在方法中,只能在方法区外,类的内部(如下),否则编译报错。
下面我们来针对这第四点来进行那个讲解:
class Window {
Window(int marker) { System.out.println("Window(" + marker + ")"); }
}
class House {
Window w1 = new Window(1);
House() {
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f() {
System.out.println("f()");
}
static Window w3 = new Window(3);
}
public class demo4 {
public static void main(String[] args) {
House house = new House();
house.f();
}
}
代码输出的结果是:
因为static修饰的变量是随着类的加载而创建,所以即使这里写在第12行代码的后面,也是先执行,可能有人会问,为什么Window(2)会在House()之前执行,这是因为,在创建Window这个类的时候,先执行静态代码块(静态代码块是为静态成员变量赋值的,也就是,先执行静态实例成员变量),然后再执行实例代码块(同上,实例代码块也是为实例变量赋值的,也就是说在遇到第一个实例变量的时候,后续的实例变量都是一起执行的),最后再执行这个类的构造方法。
1.2 static修饰的成员方法
Java中,被static修饰的成员方法称为静态成员方法,是类的方法(可以不用实例化对象,直接通过 类名.方法名 来访问),不是某个对象所特有的。
🚩🚩示例:
🔎🔎分析:通过观察,我们可以发现,被static修饰的方法不需要实例化对象。
也就是说,如果这个对象被置为空,那么这个方法的调用也是不会受到影响的(以下为牛客网的一道题):
分析:因为static是不依赖于对象的,所以这里本来应该抛空指针异常的,但是这里可以通过编译并正常运行。
🔔🔔需要特别注意的是:我们不能在被static修饰的方法中直接调用成员变量(系统会报错),因为成员变量是依赖于对象的。
如果想在静态成员方法中访问非静态的成员变量也有办法:
1.在静态方法中实例化出一个对象。
2.给静态方法传参(对象)。
同样的,如果在静态方法中,调用非静态方法需要实例化对象(如果是静态方法则可以直接调用)。
🎈🎈总结:
静态方法特性
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能直接在静态方法中访问任何非静态成员变量
🌹🌹:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性静态成员变量的初始化分为两种:就地初始化(定义的时候直接进行赋值) 和 静态代码块初始化。下面我们重点来讲解一下代码块的相关知识。
二、代码块
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:普通代码块(本地代码块),构造代码块(实例代码块),静态代码块,同步代码块(这篇未讲解)。
2.1 普通代码块
普通代码块:又称 本地代码块(定义在方法中),用法较为少见。
2.2 构造代码块
构造块:定义在类的内部,方法的外部的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
2.3静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
2.4 代码块的执行规则
我们发现实例化一个对象后执行的顺序为:静态代码块—>实例代码块—>构造方法
我们在来观察这个运行结果:
发现两者都定义classes时,按照代码的先后顺序来决定。(从中我们发现示例代码块并未被执行,也就是说没有实例化对象,实例代码块就不会被执行)
静态代码块不管生成多少个对象,其只会执行一次:
🎈🎈总结:
- 静态代码块不管生成多少个对象,其只会执行一次.
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的.
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并.
- 实例代码块只有在创建对象时才会执行.
三、 内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件。
3.1 实例内部类
实例内部类:即未被static修饰的成员内部类。(可以想象成外部类的"实例成员")
1.在实例内部类中,一般情况 是不能定义静态方法(真的不能定义)和静态变量(有回旋余地)的。
但是如果非要定义静态变量的话,这个静态变量必须被 final 修饰:
3.1.1实例化内部类
格式:
1.实例化外部类后,外部类.内部类 内部类变量名 = 外部类变量名.new 内部类类名();
2.外部类.内部类 内部类变量名 = new 外部类名.new 内部类类名()
如果当实例内部类和外部类都定义了同样的变量名时,那变量名是"听命"于谁呢?
答案:"听命"于实例内部类
如果想要访问外部类中和内部类同名的非静态的变量,就需要:外部类.this 来访问
如果是同名的静态的就需要使用 外部类类名.变量名:
每一个类都会对应一个字节码文件:在文件在out目录之下
🎈🎈总结:
1. 外部类中的任何成员都可以被在实例内部类方法中直接访问
2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
4. 实例内部类对象必须在先有外部类对象前提下才能创建
5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
3.2 静态内部类
被static修饰的内部成员类称为静态内部类。
1.如何拿到实例内部类的对象
2.不能在静态内部类中直接访问外部类中的非静态成员:
如果实在想访问就需要提供外部类对象的引用:
🎈🎈总结:
1.在静态内部类中只能访问外部类中的静态成员
2. 创建静态内部类对象时,不需要先创建外部类对象