在之前的博客中经常会出现修饰符这个词,对于Java来说,修饰符也是一个很重要的知识点,所以这篇博客就来聊一聊Java中的修饰符。
1、static修饰符
1.static变量
在类中,使用static修饰的成员变量,就是静态变量,反之为非静态变量
静态变量和非静态变量的区别
静态变量属于类的,“可以”使用类名来访问
非静态变量是属于对象的,“必须”使用对象来访问
1 public class Student{ 2 private static int age; 3 private double score; 4 public static void main(String[] args) { 5 Student s = new Student(); 6 //推荐使用类名访问静态成员 7 System.out.println(Student.age); 8 System.out.println(s.age); 9 System.out.println(s.score); 10 } 11 }
静态变量对于类而言在内存中只有一个,能被类的所有实例所共享。
实例变量对于类的每个实例都有一份, 它们之间互不影响.
1 public class Student{ 2 private static int count; 3 private int num; 4 public Student() { 5 count++; 6 num++; 7 } 8 public static void main(String[] args) { 9 Student s1 = new Student(); 10 Student s2 = new Student(); 11 Student s3 = new Student(); 12 Student s4 = new Student(); 13 //因为还是在类中,所以可以直接访问私有属性 14 System.out.println(s1.num); 15 System.out.println(s2.num); 16 System.out.println(s3.num); 17 System.out.println(s4.num); 18 System.out.println(Student.count); 19 System.out.println(s1.count); 20 System.out.println(s2.count); 21 System.out.println(s3.count); 22 System.out.println(s4.count); 23 } 24 }
在加载类的过程中为静态变量分配内存,实例变量在创建对象时分配内存,所以静态变量可以使用类名来直接访问,而不需要使用对象来访问.
2、static方法
在类中,使用static修饰的成员方法,就是静态方法,反之为非静态方法。
静态方法和非静态方法的区别
静态方法数属于类的,"可以"使用类名来调用
非静态方法是属于对象的,"必须"使用对象来调用.
静态方法"不可以"直接访问类中的非静态变量和非静态方法,但是"可以"直接访问类中的静态变量和静态方法
注意:this和super在类中属于非静态的变量.(静态方法中不能使用)
1 public class Student{ 2 private static int count; 3 private int num; 4 public void run(){} 5 public static void go(){} 6 public static void test(){ 7 //编译通过 8 System.out.println(count); 9 go(); 10 //编译报错 11 System.out.println(num); 12 run(); 13 } 14 }
非静态方法"可以"直接访问类中的非静态变量和非静态方法,也"可以"直接访问类中的静态变量和静态方法
1 public class Student{ 2 private static int count; 3 private int num; 4 public void run(){} 5 public static void go(){} 6 public void test(){ 7 //编译通过 8 System.out.println(count); 9 go(); 10 //编译通过 11 System.out.println(num); 12 run(); 13 } 14 }
思考:为什么静态方法和非静态方法不能直接相互访问? 加载顺序的问题!
父类的静态方法可以被子类继承,但是不能被子类重写
1 public class Person { 2 public static void method() {} 3 } 4 //编译报错 5 public class Student extends Person { 6 public void method(){} 7 } 8 例如: 9 public class Person { 10 public static void test() { 11 System.out.println("Person"); 12 } 13 } 14 //编译通过,但不是重写 15 public class Student extends Person { 16 public static void test(){ 17 System.out.println("Student"); 18 } 19 } 20 main: 21 Perosn p = new Student(); 22 p.test();//输出Person 23 p = new Person(); 24 p.test();//输出Perosn 25 和非静态方法重写后的效果不一样
父类的非静态方法不能被子类重写为静态方法 ;
1 public class Person { 2 public void test() { 3 System.out.println("Person"); 4 } 5 } 6 //编译报错 7 public class Student extends Person { 8 public static void test(){ 9 System.out.println("Student"); 10 } 11 }
3、代码块和静态代码块
【类中可以编写代码块和静态代码块】
1 public class Person { 2 { 3 //代码块(匿名代码块) 4 } 5 static{ 6 //静态代码块 7 } 8 }
【匿名代码块和静态代码块的执行】
因为没有名字,在程序并不能主动调用这些代码块。
匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。同时匿名代码块在每次创建对象的时候都会自动执行.
静态代码块是在类加载完成之后就自动执行,并且只执行一次
注:每个类在第一次被使用的时候就会被加载,并且一般只会加载一次.
1 public class Person { 2 { 3 System.out.println("匿名代码块"); 4 } 5 static{ 6 System.out.println("静态代码块"); 7 } 8 public Person(){ 9 System.out.println("构造器"); 10 } 11 } 12 main: 13 Student s1 = new Student(); 14 Student s2 = new Student(); 15 Student s3 = new Student(); 16 //输出 17 静态代码块 18 匿名代码块 19 构造器 20 匿名代码块 21 构造器 22 匿名代码块 23 构造器
【匿名代码块和静态代码块的作用】
匿名代码块的作用是给对象的成员变量初始化赋值,但是因为构造器也能完成这项工作,所以匿名代码块 使用的并不多。
静态代码块的作用是给类中的静态成员变量初始化赋值。
1 public class Person { 2 public static String name; 3 static{ 4 name = "tom"; 5 } 6 public Person(){ 7 name = "zs"; 8 } 9 } 10 main: 11 System.out.println(Person.name);//tom
注:在构造器中给静态变量赋值,并不能保证能赋值成功,因为构造器是在创建对象的时候才指向,但是静 态变量可以不创建对象而直接使用类名来访问.
4、创建和初始化对象的过程
Student s = new Student();
【Student类之前没有进行类加载】
1. 类加载,同时初始化类中静态的属性
2. 执行静态代码块
3. 分配内存空间,同时初始化非静态的属性(赋默认值,0/false/null)
4. 调用Student的父类构造器
5. 对Student中的属性进行显示赋值(如果有的话)
6. 执行匿名代码块
7. 执行构造器
8. 返回内存地址
注:子类中非静态属性的显示赋值是在父类构造器执行完之后和子类中的匿名代码块执行之前的时候
5、静态导入
静态导包就是java包的静态导入,用import static代替import静态导入包是JDK1.5中的新特性。
意思是导入这个类里的静态方法。
好处:这种方法的好处就是可以简化一些操作,例如打印操作System.out.println(…);就可以将其写入一 个静态方法print(…),在使用时直接print(…)就可以了。但是这种方法建议在有很多重复调用的时候使用,如果仅 有一到两次调用,不如直接写来的方便。
2、final修饰符
1、修饰类
用final修饰的类不能被继承,没有子类。
例如:我们是无法写一个类去继承String类,然后对String类型扩展的,因为API中已经被String类定义为final 的了.
我们也可以定义final修饰的类:
1 public final class Action{ 2 } 3 //编译报错 4 public class Go extends Action{ 5 }
2、修饰方法
用final修饰的方法可以被继承,但是不能被子类的重写
例如:每个类都是Object类的子类,继承了Object中的众多方法,在子类中可以重写toString方法、equals方 法等,但是不能重写getClass方法 wait方法等,因为这些方法都是使用fianl修饰的。 我们也可以定义final修饰的方法:
1 public class Person{ 2 public final void print(){} 3 } 4 //编译报错 5 public class Student extends Person{ 6 public void print(){ 7 } 8 }
3、修饰变量
用final修饰的变量表示常量,只能被赋一次值.其实使用final修饰的变量也就成了常量了,因为值不会再变 了。
3、abstract修饰符
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那 么该类就是抽象类。
1、抽象类和抽象方法的关系
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
2、语法
1 public abstract class Action{ 2 public abstract void doSomething(); 3 } 4 public void doSomething(){...}
对于这个普通方法来讲:
"public void doSomething()"这部分是方法的声明
"{...}"这部分是方法的实现,如果大括号中什么都没写,就叫方法的空实现
声明类的同时,加上abstract修饰符就是抽象类
声明方法的时候,加上abstract修饰符,并且去掉方法的大口号,同时结尾加上分号,该方法就是抽象方法。
3、特点及作用
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
注:子类继承抽象类后,需要实现抽象类中没有实现的抽象方法,否则这个子类也要声明为抽象类。
注:子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。