一、static
1.1案例引入
如果说我们想要知道一个类创建了多少个实例对象,那我们就定义一个count 的变量,每当创建实例对象时,count++。
public class Test01 {
public static void main(String[] args) {
int count = 0;
A a1 = new A();
count++;
A a2 = new A();
count++;
System.out.println("有" + count + "个A对象");
}
}
class A{
}
这样写的话非常不安全,也体现不了封装,很low。
这时候不禁就会思考是否java中存在某个方式,能解决这个问题捏。
那就是static关键字!
我们现在可以这样写:
public class Test01 {
public static void main(String[] args) {
//int count = 0;
A a1 = new A();
//count++;
A a2 = new A();
//count++;
System.out.println("有" + a1.count + "个A对象");//输出2
}
}
class A{
public static int count = 0;
public A() {
count++;
}
}
在这里我们定义了一个变量count,是一个类变量(静态变量)
该变量的特点就是会被A类的所有对象实例共享
即:a1.count 和 a2.count都为2
因为也叫类变量,所以也可以这样调用A.count
1.2类变量(静态变量)
要点:1)static变量是同一个类所有对象共享
2)static变量,在类加载的时候就生成了(在代码块时会具体叙述)
1.3类方法(静态方法)
静态方法的调用与静态变量一样,并且与所有对象共享
注意事项:1)static方法中无this参数,即不能使用this
2)static方法中不能使用super
3)static方法中只能访问静态变量或静态方法
二、代码块
2.1案例引入
我们现在想要对一个类,在每次创建对象时候输出一个“hello,world‘’,那就在每个构造器中写一个输出语句。
class A{
private int n1;
public A() {
System.out.println("hello,world");
}
public A(int n1) {
System.out.println("hello,world");
this.n1 = n1;
}
}
此时我们发现,代码冗余,每个构造器都要写,很low,此时我们可以用代码块来解决。
public class Test01 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A(1);
}
}
class A{
{
System.out.println("hello,world");
}
private int n1;
public A() {
}
public A(int n1) {
this.n1 = n1;
}
}
输出如下:
2.2基本介绍
1)如果多个构造器中都有 重复的语句,可以抽取到初始化块中,提高代码的重用性
2)代码块又称初始化块,属于类成员,类似于方法。
3)代码块没有方法名、返回值、参数,只有方法体
4)代码块不能通过显示调用(因为名字都没,咋调用),而是类加载时或创建对象时隐式调用。
5)代码块的调用顺序优先于构造器
2.3static代码块
static代码块也叫静态代码块,随着类的加载而执行,只会执行一次,普通代码块是每创建一个对象就执行。
注意:只执行一次是说当你使用过这个类以后,再使用的话也不会执行
只会输出一个hello,world
2.4类加载
那么类啥时候加载呢?
创建对象时,即new一个对象
创建子类对象实例,父类也会被加载
使用类的静态成员是,即使用里面静态方法、静态属性
总得来说就是一旦跟这个类有关时就会加载!
2.5创建一个对象
创将一个对象时在一个类中的调用顺序:
第一步:调用静态代码块和静态属性初始化,这两个人调用优先级一样,调用顺序根据定义顺序执行。
第二步:调用普通代码块和普通属性初始化,这两个人调用优先级一样,调用顺序根据定义顺序执行。
第三步:调用构造方法。
2.6烦人对象创建
当加上继承关系的时候,要遵守继承的调用原则。
public class test {
public static void main(String[] args) {
new BBB();
//1.进行类的加载调用静态成员
//由于继承关系,先加载父类在加载子类
//2.创建对象
//先进行成员属性的默认初始化
//再进行成员属性的显示初始化和普通代码块初始化(调用顺序由定义顺序决定)
//最后进行构造器中剩余内容初始化
//!!遵守继承初始化调用原则
}
}
class AAA {
private int n = fun1();
private static int sn = fun2();
static {System.out.println("AAA的静态代码块的调用~");};
{System.out.println("AAA的普通代码块的调用~");};
//隐藏了
//super()
//普通代码块调用
public AAA() {System.out.println("AAA 构造器调用");}
public int fun1(){System.out.println("父类普通成员属性显示初始化"); return 1;}
public static int fun2(){System.out.println("父类静态成员属性显示初始化"); return 1;}
}
class BBB extends AAA{
private int n = fun1();
private static int sn = fun2();
static {System.out.println("BBB的静态代码块的调用~");};
{System.out.println("BBB的普通代码块的调用~");};
//隐藏了
//super()
//普通代码块调用
public BBB() {System.out.println("BBB 构造器调用");}
public int fun1(){System.out.println("子类普通成员属性显示初始化"); return 1;}
public static int fun2(){System.out.println("子类静态成员属性显示初始化"); return 1;}
}
输出如下:
分析:
第一句:首先创建B类对象,由于继承会先加载父类,所以调用父类的静态成员,根据定义顺序,调用fun2().
注意:子类重写了父类的fun2(),但是子类的方法没有加载,所以调用的还是父类的fun2()
第二句:调用父类中的静态代码块
第三句:根据定义顺序,先调用子类静态属性的初始化
第四句:调用子类中的静态代码块
第五句:其实子类中的构造器前隐藏了两条语句:super() 和 自己普通代码块的调用。所以先执行了super()。父类的构造器同理也隐藏了。由于父类属性在普通代码块前,所以先调用父类属性的初始化,但是因为父类子类都已经加载完毕,此时发生运行类型是B类,所以执行子类中的重写方法。
第六句:执行父类普通代码块
第七句:输出父类构造器本生的语句,至此父类结束
第八、九、十句同理。
总结:调用顺序
父类静态代码块和静态属性
子类静态代码块和静态属性
父类普通代码块和普通属性
父类构造器
子类普通代码块和普通属性
子类构造器
三、final
3.1基本介绍
final可以修饰类、属性。方法
final修饰类,此类不能被继承
final修饰父类中方法,此方法不能被子类重写
final修饰变量,此时就变为了常量,不能被修改了
3.2注意事项
final修饰的属性得初始化赋值,以后不能修改(变常量了)
1)赋值位置:
对于非静态属性:1. 定义时
2.代码块
3.构造器
对于静态属性:1定义时
2.静态代码块
说明:因为静态属性在类加载时就会初始化,且只执行一次,如果在构造器中赋值,就会存在创建对象时就赋值,所以不能再构造器中。
2)fianl不能修饰构造器
3)fianl和static搭配,效率会更高,不会导致类加载,编译器做了优化处理
public class Test01 {
public static void main(String[] args) {
System.out.println(A.n1);
}
}
class A{
// public final static int n1 = set();
public final static int n1 = 100;
static {
System.out.println("hello");
}
public static int set(){
System.out.println("111");
return 10;
}
}
只会输出n1的值
四、最后
本人是java小白,如有问题,欢迎指出。