一、静态变量
1.1为什么要有静态变量
现在有一群小朋友在做游戏,不是有新的小朋友加入,请问如何知道现在共有多少人在完?看这段代码:
public class first {
public static void main(String[] args) {
int count = 0;
child child1 = new child("小明");
child1.join();
++count;
child child2 = new child("小红");
child2.join();
++count;
child child3 = new child("小李");
child3.join();
++count;
}
}
class child{
private String name;
public child(String name){
this.name=name;
}
public void join(){
System.out.println(name + "加入了游戏");
}
}
这种实现方法中,count是一个独立于对象的变量,不符合Java面向对象的特点,并且访问起来比较麻烦。如果有一个变量能够表示对象的总个数,我们在创建一个对象时,就把这个变量加1,并且这个变量是所有同类对象共享的就ok了。因此这里引入了静态变量。
1.2什么是静态变量
在定义一个成员变量时,如果使用static关键字修饰它,则该成员变量又可被称为静态变量或类变量,它是该类的所有对象共享的变量,任何一个该类的对象访问它时,取到的都是相同的值。它随着类的加载而创建,即使没有创建对象实例也可以访问。
public class first {
public static void main(String[] args) {
child child1 = new child("小明");
child1.join();
++child.count;
child child2 = new child("小红");
child2.join();
++child.count;
child child3 = new child("小李");
child3.join();
++child.count;
}
}
class child{
private String name;
public static int count = 0;//静态变量
public child(String name){
this.name=name;
}
public void join(){
System.out.println(name + "加入了游戏");
}
}
1.3静态变量内存分析
我们知道,Java的内存空间被分为栈区、堆区和方法区,静态变量具体存在于那一块由JDK决定。
- JDK8以前,静态变量存放在方法区中。
- JDK8以后,类加载后会在堆中生成一个对应的class对象,这时,静态变量存放在堆中反射的class对象的尾部。
不管静态变量在哪,我们只要记住:
- static变量由同一个类的所有对象共享。
- static变量在类加载的时候就生成了。
1.4静态变量的使用
定义的基本语法:
访问修饰符 static 数据类型 变量名;//推荐
static 访问修饰符 数据类型 变量名;//也可以
访问的基本语法:
静态变量的访问修饰符的访问权限和范围与普通属性一样。
类名.静态变量名;//推荐
对象名.静态变量名;//也可以
二、静态方法
2.1静态方法的使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。如果我们希望不创建实例,也可以调用某个方法,那么将其设计为静态方法就非常合适。
2.2静态方法的使用
静态方法也叫类方法。
定义的基本语法:
访问修饰符 static 数据返回类型 方法名(){ }//推荐
static 访问修饰符 数据返回类型 方法名(){ }
调用的基本语法,前提是满足访问修饰符的访问权限和范围:
类名.静态方法名
对象名.静态方法名
比如说:
public class first {
public static void main(String[] args) {
System.out.println(Mytools.calsum(10.2,52.3));//62.5
}
}
class Mytools{
public static double calsum(double n1, double n2){
return n1 + n2;
}
}
这里直接才类名.静态方法名就成功调用了该函数。
2.3静态方法的细节
- 静态方法和普通方法都随着类的加载而加载,将结构信息存储在方法区。类中没有this的参数,普通方法中也许存在。
- 静态方法可以通过类名调用,也可以通过对象名调用。
- 普通方法和对象有关,不能通过类名调用。
- 静态方法中不允许使用和对象有关的关键字,比如this和super。
- 静态方法只能访问静态变量和静态方法,而普通方法既可以访问普通变成员也可以访问静态成员。
三、main方法
3.1main语法说明
public static void main(String[] args)
- main方法由Java虚拟机调用,所以访问权限必须是public。
- Java虚拟机在调用时不会创建对象,所以该方法必须是static。
- args数组保存执行java命令时传递给所运行的类的参数。
3.2main特别说明
- 在main方法中,我们可以直接调用main方法所在类的静态方法或者静态属性。
- 但是,不能直接访问该类的非静态成员,必须创建一个该类的实例对象后,才能通过这个类区访问类中的非静态成员。
public class first {
int age = 10;
static int k = 20;
public static void main(String[] args) {
first obj = new first();
System.out.println(age);//不可以
System.out.println(obj.age);//可以
System.out.println(k);//可以
}
}
四、代码块
4.1为什么要有代码块
如果多个构造器中都有重复的语句,那么就可以将它们抽取到初始化块中,提高代码的重用性,相当于另一种形式的构造器,可以做初始化的操作。
4.2什么是代码块
代码化块又称为初始化块,属于类中的成员(即类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。但它和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不需要通过对象或者类显示调用,而是加载类时,或创建对象时隐式调用。
基本语法:
[修饰符]{
代码;
};
- 这里的修饰符可写可不写,要写的话只能是static。
- 使用static修饰的被称为静态代码块,没有static修饰的,叫做普通代码块。
- ;号可写可不写。
4.3代码块使用细节
- static代码块又叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而进行,并且只会执行一次。如果是普通代码块,每创建一个对象就会执行一次。
- 类什么时候被加载,类即使多次被加载,也只会执行一个static代码块:
- 创建对象实例时。
- 创建子对象实例时,父类也会被加载。
- 使用类的静态成员时。
- 子类使用静态成员时,父类也会被加载。
- 普通代码块,在创建实例时,会被隐式的调用,被创建一次调用一次,如果只是使用类的静态成员,普通代码块并不会执行。
- 对象创建时,一个类的调用顺序是:
- 首先调用静态代码块和静态属性初始化,它们的优先级一样,按定义的顺序调用。
- 再调用普通代码块和普通属性初始化,按定义顺序调用。
- 调用构造方法。
- 构造器的最前面,隐含了super方法和调用普通代码块以及普通属性初始化。
public class first { public static void main(String[] args) { B b = new B();//输出为01->02->03->04 } } class A{ static{ System.out.println("01"); } { System.out.println("02"); } public A(){ //代码块 } } class B extends A{ { System.out.println("03"); }; public B(){ //super() //调用普通代码块和普通属性初始化 System.out.println("04"); } }
创建一个B类的对象,B类本身没有静态代码块,所以直接加载A类的静态代码块,第一个输出为01,然后B的构造器中,会先调用A的构造器,A的构造器调用A自己的普通代码块,输出为02,然后调用B自己的普通代码块,输出03,最后执行自己的构造器输出04。
- 一个子类创建一个子类对象时,它们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造起的调用顺序如下执行:
- 父类的静态代码块和静态属性。
- 子类的静态代码块和静态属性。
- 父类的普通代码块和普通属性初始化。
- 父类的构造方法。
- 子类的普通代码块和普通属性初始化。
- 子类的构造方法。
- 静态代码块只能调用静态成员,普通代码块可以调用任意成员。
五、final关键字
5.1final关键字使用场景
- 使一个类不能再被继承。
- 使一个方法不能被重写。
- 当不希望类的某个属性的值被修改。
- 当不希望某个局部变量被修改。
5.2final关键字使用细节
- final修饰的属性又叫做常量,一般用 xx_xx_xx来命名
- final修饰符的属性再定义时,必须赋初值,并且以后不能再修改,赋值可以如下位置之一
- 定义时。
- 在构造器中。
- 在代码块中。
- 如果final修饰的属性是静态的,则初始化的位置只能是定义时或者静态代码块中,不能在构造器中赋值,因为静态属性在定义是需要被赋初值,但是构造器要在实例化的时候才会被调用。
- final类不能继承,但是可以实例化对象。
- 如果类不是final类对象,但是具有final方法,则该方法虽然不能重写,但是可以被继承。
- 如果一个类已经是final类了,就没必要再给它的方法添上final关键字。
- final不能修饰构造器。
- final和static往往搭配使用,这样不会导致类加载,可以提高效率。