三个修饰符
一、abstract
abstract:抽象的
例如生活中有很多动物,狗、猫、猪等等,但是这些都统称为动物,动物并不是一个实实在在的实例,所以这里的动物这个词是我们抽象出来的一个名字,而狗、猫等这些动物属于这个拥有动物特征所建立出的一个具体实例。
1.1 修饰类
有的类是抽象出来的概念,只能作为父类存在,而不应该直接创建对象,此时我们可以在类上加入abstract关键词,设置为抽象类,不能直接创建对象,只能作为父类,让其子类继承。
//abstract修饰的类无法创建对象 public abstract class class_Abstract { }
1.2 修饰方法
abstract修饰的方法为抽象方法,抽象方法没有方法体,由子类去定义抽象方法的内容,一般我们将子类重写父类的一个方法定义为抽象方法。
例如:之前我们创建的Animal、Dog、Cat类,我们可以将Animal定义为抽象类,将他的eat方法可以定义为抽象方法,因为子类都会去重写这个类。
public abstract class class_Abstract { abstract void method_Abstract(); }
注意:
-
一个抽象类可以没有抽象方法;
-
一个抽象方法必须在抽象类中;
-
抽象方法不能用private,否则无意义,因为抽象方法就是为了继承给子类重写;
-
当子类继承一个有抽象方法的抽象类时,会报错,解决方案如下:
-
将子类也设置为抽象类
-
重写父类中所有的抽象方法
-
二、final
final:最终的、最后的、终结的
2.1 修饰类
当修饰类时表示该类不可被继承,因此注意final和abstract无法一起使用,abstract就是为了给子类继承编写,但是final无法继承。
public final class class_Abstract { }
2.2 修饰方法
当修饰方法时表示该方法不可被重写。
public final void method_Abstract(){ }
2.3 修饰变量
表示该变量一旦赋值就不可改变,称为常量。
2.3.1 修饰属性
当修饰属性后,该属性必须要在创建对象后,必须有值。赋值时机:
可在属性定义上赋予初始值
public final class class_Abstract { public final String s1 = "aaaa"; }可在类的构造方法中赋值
public final class class_Abstract { final String s1; public class_Abstract(){ s1 = "aaaa"; } }
可在代码块中赋值
public final class class_Abstract { final String s1; { s1 = "aaa"; } }注意:一旦代码块中赋值后,就不能在构造方法中再次赋值,因为常量不能再次赋值,代码块比构造方法先运行。
2.3.2 修饰局部变量
当修饰局部变量时,局部变量需要先声明后赋值或者声明时赋值。
2.3.3 修饰方法参数
当修饰方法参数时,表示传入的参数无法更改
2.3.4 常量
在类中我们一般会定义变量,声明final修饰,视作常量;
项目中我们将经常出现的一个常数或者字符串,不希望在过程中改变的,我们可以定义为常量。
常量定义时单词全大写,多个单词间用下划线隔开。
在java中有一些提供的常量,例如Math.PI
import static java.lang.Math; public final class class_Abstract { public void aa(){ System.out.println(Math.PI); } }
三、static
static:静态的
3.1 修饰属性
表示该属性为静态属性,也称为类属性。
同一个类中共享一个类属性空间(所有对象使用的同一个类属性),也可以用对象操作,但是不推荐。
注意:不能修饰局部变量,不需创建对象就可使用类属性,创建对象也可使用类属性。
public class Demo { public static void main(String[] args) { //类属性可以用对象调用也可以用类调用 System.out.println(A.name); A a = new A(); System.out.println(a.name); //因为类属性共享一个内存空间,所以使用对象修改类属性值,所有调用类属性都会改变 a.name = "修改后的类属性"; System.out.println(a.name); System.out.println(A.name); } } public class A { static String name = "我是类属性"; }
3.2 修饰方法
表示为静态方法,也称类方法。
可以直接使用类名调用,也可创建对象调用。
注意:
静态方法不需创建对象就可使用;非静态方法需要创建对象调用
静态方法可以调用静态的方法和属性,不能调用非静态的方法和属性
非静态方法可以调用静态方法和属性,也可调用非静态的方法和属性;
静态方法不能使用this和super修饰,因为静态方法不用创建对象,this和super需要创建对象使用。
静态方法可以被继承,但是不能重写,所以也无法多态。
public class A { static String name = "我是类属性"; String name1 = "我不是类属性"; static void S1(){ System.out.println(name); // System.out.println(name1);//会报错,因为name1不是静态属性 } //非静态方法可以使用所有属性 public void S2(){ System.out.println(name); System.out.println(name1); } }
3.3 修饰代码块
3.3.1 动态代码块
类中由一对大括号组成的代码块称为动态代码块,创建对象时执行,可执行多次,每次创建对象都会执行。
执行流程:
初始化属性
执行动态代码块
执行构造方法
public class Demo { public static void main(String[] args) { new A(); } } public class A { String name = "我是属性"; { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(name); System.out.println("我是动态代码块"); } public A(){ System.out.println("我是构造方法"); } //我是属性 //我是动态代码块 //我是构造方法 }
3.3.2 静态代码块
static关键字修饰的代码块为静态代码块,类加载时执行,只执行一次。
在创建对象时会加载类
类加载:将类信息加载到方法区,以便得到对象创建时所需得空间大小,方便创建对象。
执行流程:
静态属性
静态代码块
实例属性
执行动态代码块
执行构造方法
public class Demo { public static void main(String[] args) { new A(); //我是静态属性 //我是静态代码块 //我是实例属性 //我是动态代码块 //我是构造方法 } } public class A { String name = "我是实例属性"; static String name1 = "我是静态属性"; { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(name); System.out.println("我是动态代码块"); } { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(name1); System.out.println("我是静态代码块"); } public A(){ System.out.println("我是构造方法"); } }
3.4 继承时的加载顺序
面试题:当A继承了B类,那么创建A对象,他们之中的加载顺序是什么样的??
加载流程:
-
父类静态属性
-
父类静态代码块
-
子类静态属性
-
子类静态代码块
-
父类实例属性
-
父类动态代码块
-
父类构造方法
-
子类实例属性
-
子类动态代码块
-
子类构造方法
由上执行流程可知,为什么类方法无法调用非静态属性。
public class Demo { public static void main(String[] args) { new A(); //我是父类静态属性 //我是父类静态代码块 //我是子类静态属性 //我是子类静态代码块 //我是父类实例属性 //我是父类动态代码块 //我是父类构造方法 //我是子类实例属性 //我是子类动态代码块 //我是子类构造方法 } } public class A extends B{ String name = "我是子类实例属性"; static String name1 = "我是子类静态属性"; { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(name); System.out.println("我是子类动态代码块"); } static { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(name1); System.out.println("我是子类静态代码块"); } public A(){ System.out.println("我是子类构造方法"); } } public class B { String n1 = "我是父类实例属性"; static String n2 = "我是父类静态属性"; { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(n1); System.out.println("我是父类动态代码块"); } static { //将name放到此处,可以看出执行动态代码块前是否先初始化属性,否则会报错 System.out.println(n2); System.out.println("我是父类静态代码块"); } public B(){ System.out.println("我是父类构造方法"); } }