static
1、static 是一个关键字,同时也是一个修饰符
2、static 可以修饰什么?
(1)属性
(2)方法
(3)代码块
(4)内部类
3、static修饰属性
用static修饰的属性,称之为静态变量、类变量。
静态变量和非静态变量的属性:
(1)存储的位置不同
非静态属性:堆
静态变量:方法区
(2)值的初始化(赋值)的时机是不同的
非静态的属性:创建实例对象时,在()实例初始化方法中完成初始化
静态变量:在类初始化的时候,在()类初始化方法中完成的初始化
静态变量的初始化比非静态的要早
(3)共享性不同
非静态的属性:每一个对象是独立的,各自存了一份
静态变量:所有该类的对象共享同一份
(4)生命周期不同
非静态的属性:随着创建对象而存在,当对象被垃圾回收器收回就消失
静态变量:随着类的初始化而初始化,随着类的卸载而卸载
静态变量的生命周期 = 类的生命周期
非静态的属性的生命周期 = 对象的生命周期,每一个对象的生命周期是独立的
(5)get/set不同
非静态的属性,对应get/set也是非静态的,如果局部变量与属性重名了用“this.”区别
静态变量:对应get/set也是静态的,如果局部变量与属性重名了,用“类名.”区别
4、static修饰方法
static修饰的方法 成为静态方法、类方法
(1)可以通过“类名.”调用,当然了也可以通过“对象.”调用
(2)静态方法方法中是不可以出现this,super这些关键字的
(3)静态方法中是不能直接使用本类中的非静态的成员(属性、方法、内部类)
(4)静态方法是不能被重写的
5、static修饰的代码块
(1)作用
代码块的作用:为属性初始化
代码块分为非静态代码块和静态代码块
非静态代码块的作用为非静态的属性初始化
静态代码块的作用,为静态属性,静态变量初始化
(2)语法结构
【修饰符】 class 类名 【extends 父类】{
{
非静态的代码块
}
static{
静态代码块
}
}
(3)静态代码块执行的特点是什么
A.无论创建几个对象,静态代码块只执行一次,如果有多个静态代码块按照顺讯执行
B.静态代码块肯定是优先与非静态代码快的
因为静态代码块的执行是在类初始化是在类初始化是执行,类初始化执行的是一个叫做的类初始化方法类初始化的<clinit>()方法由如下两个部分组成 第一 静态变量的显式赋值 第二 静态代码块 第三 第二部分谁在上面谁先执行,按照顺序想、执行
C.子类初始化时,如果发现父类还没有初始化,会先初始化父类,即先执行父类的方法
public class TestStatic {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
c1.name = "张三";
c2.name = "李四";
c1.country = "中国";
c2.country = "中华人名共和国";
System.out.println(c1.country + c1.name);
System.out.println(c2.country + c2.name);
}
}
class Chinese{
static String country;
String name;
public static String getCountry() {
return country;
}
public static void setCountry(String country) {
Chinese.country = country;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
说明一
这里就涉及到类的初始化,两部分
(1)静态变量的显式初始化
i = getInt();
(2)静态代码块
System.out.println(“静态代码块”);
类初始化只执行一次
public class TestClinit1 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
}
}
class A{
private static int i = getInt();
static {
System.out.println("静态代码块");
}
public static int getInt() {
System.out.println("A类的getInt()方法");
return 1;
}
}
说明二
调用D类的静态方法,先要初始化D类
类的初始化只需要一次
但是方法是调用一次就执行一次
public class TestClinit2 {
public static void main(String[] args) {
D.test();
D.test();
}
}
class D{
static {
System.out.println("(1)静态代码块");
}
public static void test() {
System.out.println("(2)静态方法");
}
}
说明三
1、 这里就涉及到类的初始化,两部分
(1)静态变量的显式初始化
i = getInt();
(2)静态代码块
System.out.println(“静态代码块”);
2、 类初始化只执行一次
3、子类初始化时如果父类没有初始化就会先初始化父类,就是执行父类的
public class TestClinit3 {
public static void main(String[] args) {
SubB s1 = new SubB();
SubB s2 = new SubB();
}
}
class B{
private static int i = getInt();
static {
System.out.println("静态代码块");
}
public static int getInt() {
System.out.println("B类的getInt()方法");
return 1;
}
}
class SubB extends B {
private static int j = getIntValue();
static {
System.out.println("静态代码块");
}
public static int getIntValue() {
System.out.println("SubB类的getIntValue()方法");
return 1;
}
}
练习一
被静态修饰的在类的初始化的时候就已经加载了,所以先是静态的加载出来
然后在按照顺序进行
public class TestStaticExer1 {
public static void main(String[] args) {
Son s = new Son();
}
}
class Father{
static {
System.out.println("(1)父类的静态代码块");
}
{
System.out.println("(2)父类的构造器");
}
Father(){
System.out.println("(3)父类的无参构造");
}
}
class Son extends Father{
static {
System.out.println("(4)子类的静态代码块");
}
{
System.out.println("(5)子类的构造器");
}
Son(){
System.out.println("(6)子类的无参构造");
}
}
/*
(1)父类的静态代码块
(4)子类的静态代码块
(2)父类的构造器
(3)父类的无参构造
(5)子类的构造器
(6)子类的无参构造
*/
练习二
public class TestStaticExer2 {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Fu{
private static int i = getNum("(1)i");
private int j = getNum("(2)j");
static {
print("(3)父类静态代码块");
}
{
print("(4)父类构造代码块");
}
Fu(){
print("(5)父类构造器");
}
public static void print(String str) {
System.out.println(str + "->" + i);
}
public static int getNum(String str) {
print(str);
return ++i; //1 2
}
}
class Zi extends Fu{
private static int k = getNum("(6)k");
private int h = getNum("(7)h");
static {
print("(8)子类静态代码块");
}
{
print("(9)子类构造代码块");
}
Zi(){
print("(10)子类构造器");
}
public static void print(String str) {
System.out.println(str + "->" + k);
}
public static int getNum(String str) {
print(str);
return ++k;//1 2
}
}
/*
(1)i
(3)父类静态代码块 -> 0
(6)k
(8)子类静态代码块
(2)j
(4)父类构造代码块 -> 1
(5)父类构造器 -> 1
*/
练习三
(这个主要讲的就是关于先声明后使用的问题,如果就是不先声明,也就是我先使用,后声明怎么办呢,在静态中虽然类加载的时候就已经加载了静态的声明了,但是在java中还是认为这个是后声明,在java的开发文档8.3.3(第8版)中有介绍)
public class TestStaticExer3 {
public static void main(String[] args) {
MyClass obj = new MyClass();
}
}
class MyClass{
static {
i = 100;//可以的
// i++;//报错,i是静态的,虽然是类存在的时候就已经加载了,
//但是需要先声明在使用,如果没有声明静态的就得用 类名. 来调用的,这个是java规定的
MyClass.i++;
// System.out.println("(1)静态代码块i=" + i);//报错
//通俗易懂的方式就是,这个没有提前声明的变量 这个i 只能出现在“=”等号的左边不可以出现在等号的右边
System.out.println("(1)静态代码块i=" + MyClass.i);
}
{
j = 100;//可以的
// j++;//报错,同样的这个的声明在后面,所以只能出现在等号的左边,不可以出现在等号的右边
//如果非要使用这个样子去使用的话,非静态的就用this. 调用。
this.j++;
// System.out.println("(2)静态代码块j=" + j);//报错
System.out.println("(1)静态代码块j=" + this.j);
}
MyClass(){
j = 200;
j++;
System.out.println("(3)构造器j=" + j);
}
private static int i = getNum("(4)i");
private int j = getNum("(5)j");
public static void print(String str) {
System.out.println(str + "->" + i);
}
public static int getNum(String str) {
print(str);
return ++i;
}
}