static 修饰符可以用来修饰类的成员变量、 成员方法、 代码块;用 static 修饰的成员变量表示静态变量, 直接使用类名来访问;用 static 修饰的成员方法表示静态方法, 直接使用类名来访问;用 static 修饰的代码块表示静态代码块, JVM 加载类时就会执行该代码块。
被 static 修饰的成员归类所有:它不依赖于类的特定实例, 被类的所有实例所共享;只要该类被加载, JVM 就能根据类名在方法区定位到它们。
static 变量(类变量):被 static 修饰的变量称为静态变量或类变量(类属性),类属性属于整个类。也就是说当系统第一次使用类时(类的准备阶段)即为该类属性分配内存空间, 类属性开始生效,直到该类被卸载, 该类的类属性所占的内存才被 JVM 的垃圾回收机制回收。
类属性的生存范围几乎等同于该类的生存范围:当类初始化完成后, 类属性也被初始化完成,当类被卸载, gc 才回收其所占的内存空间。
static 变量(类变量):可以通过类来访问 ( 即类名.类属性名)。当类初始化完毕后, 类属性也初始化完毕,所以可以通过类名访问类属性;也可以使用对象来访问 ( 即对象名.类属性名),使用对象名来访问类属性并不是访问该对象所具有的属性,当系统创建该类的对象时不会再度为类属性分配内存,也不会再度对类属性进行初始化。对象根本不包括其所属类的类属性:使用对象访问类属性是一种假象,本质上, 通过对象访问类属性还是要转换成使用类访问类属性。
静态变量和实例变量的区别:对于静态变量:运行时 JVM 只为其分配一次内存,(即在加载类的过程中完成静态变量的内存分配);静态变量可以通过类名直接访问;静态变量会被类的所有实例所共享。对于实例变量:每创建类的一个实例, 就会为实例变量分配内存;实例变量对于每个实例而言是专属的。
public class Sheep {
public static String school = "大肥羊学校";
private String name;// 实例属性(非静态属性)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestSheep {
public static void main(String[] args) {
// 在加载类后 就创建了 school变量,并赋予相应的值(暂时的理解)
// 只要用到school 那么就只有这么一份
System.out.println(Sheep.school);
// 创建Sheep 实例,并 为实例属性 name 开辟空间,赋予相应的默认值
Sheep s = new Sheep();
System.out.println(s.getName());
s.setName("喜羊羊");
System.out.println(s.getName());
// 创建Sheep 实例,并 为实例属性 name 开辟空间,赋予相应的默认值
//s = new Sheep();// 栈中的变量,声明一次,可以接收不同的变量
Sheep s2 = new Sheep();
System.out.println(s2.getName());
s2.setName("懒羊羊");
System.out.println(s2.getName());
Sheep s3 = null;
System.out.println(s3.school);// 因为访问的是静态属性
}
}
类的信息放在方法区中,就是所有的静态的东西都放在方法区中。方法的内容放在方法区中,代码公共部分放在方法区中。即使在堆区域不存放任何东西,那么静态的东西都放在方法去中,通过类名可以去访问。即使通过对象名进行访问类中的某一个属性的时候,那个属性也是存放在方法区中。
代码块
/**
* 一个类中可以包含:
* 属性:静态属性 和 实例属性
* 方法:静态方法 和 实例方法
* 构造:与类名同名、没有返回类型的特殊方法
* 代码块:静态代码块 和 普通代码块
* 在同一个类中,代码块的执行顺序,跟书写的顺序有关,谁在前谁先
执行
* 对于静态代码块,同一个类中谁先书写谁先执行
* 对于静态代码块,如果子类继承了父类,则所有的静态代码块按顺序
依次执行,先父类,后子类
* 等到所有的静态代码块执行完,如果构造了一个对象,则执行 普通代
码块
*/
public class Father {
private String name = "张三丰";
//protected String school;
protected static String school;
public Father(){
System.out.println("Father:构造方法," + this.name);
}
{// 如果没有加上花括号,这样是错误的。注意,这里没有static修饰的代码块,就是普通代码块
System.out.println("Father:我是普通代码块1");
this.name = "张三丰";// 可以通过 代码块 对非静态属性进行初始化,也就是赋值
}
{// 如果没有加上花括号,这样是错误的。注意,这里没有static修饰的代码块,就是普通代码块
System.out.println("Father:我是普通代码块A");
}
static{// 有static 修饰的代码块就是静态代码块,只执行一次
System.out.println("Father:静态代码块1");
//school = "学校"; // 静态代码块 只能访问静态属性
school = "学校";
}
static{// 有static 修饰的代码块就是静态代码块,只执行一次
System.out.println("Father:静态代码块A");
}
}
TestFather:
public class TestFather {
public static void main(String[] args) {
// 执行顺序:静态代码块、普通代码块、构造方法
Father f1= new Father();
Father f2= new Father();
}
}
如果要有继承的话,有如下的例子:
public class Child extends Father{
public Child(){
super();
}
static {
System.out.println("child 静态代码块");
}
}
TestChild:
public class TestChild {
public static void main(String[] args) {
Child c1 = new Child();
}
}
final 表示”不可修改的”, final 可以修饰非抽象类、 非抽象成员方法、 变量。 注: 被 final 修饰的类, 不能被继承, 没有子类, 比如 String 类;被 final 修饰的方 法不能被子类的方法覆盖;被 final 修饰的变量表示常量, 只能被赋值一次。不能使 用 final 来修饰构造方法,因为构造不可能被继承, 因此没有必要限制为最终的、 不 可更改的。
/**
* 所有的 final 修饰的变量,不论静态、实例、还是局部变量,其取值都不能修改
*/
public class TestFinal1 {
public static final double pi = 3.14;// 静态变量 pi 的值 不能再被修改
public final int a = 100; // 实例变量 a 的值 不能再被修改该
public static void main(String[] args) {
// final 修饰局部变量
final int a = 100;
//a = 200; // The final local variable a cannot be assigned
System.out.println("a = "+a);
// 尝试修改 静态变量 的值
//TestFinal1.pi = 6.28; // The final field TestFinal1.pi cannot be assigned
TestFinal1 tf = new TestFinal1();
// 尝试修改实例变量的值
//tf.a = 200; // The final field TestFinal1.a cannot be assigned
// 但是是可以使用的
System.out.println(tf.a);
}
}
final 修饰的类:
// 这个类是最终的,不可更改的一个类
public final class LuoZi {
private String name;
}
// The type XinLuoZi cannot subclass the final class LuoZi
public class XinLuoZi /* extends LuoZi */ {
}
final 修饰的方法:
public class Father {
public void eat( String food){
System.out.println("吃" + food);
}
public final void run( ){
System.out.println("跑");
}
}
public class Child extends Father{
protected String name;
public void eat(String food){
System.out.println("wochi " + food);
}
// 尝试重写 继承自 父类的 final 修饰的分法
// Cannot override the final method from Father
// public void run(){
// }
}