三个修饰符
一、abstract
abstract,抽象的。
1.1 修饰类
有的类是抽象出来的概念,只能作为父类存在,而不应该直接创建对象,此时,可以在类上加上abstract关键,设置该类为抽象类,不能直接创建对象,只能作为父类。
public abstract class Animal { private String name; public void eat() { System.out.println("动物在吃..."); } }
public class Demo1 { public static void main(String[] args) { // Animal a = new Animal(); // 报错,不能直接创建对象 Animal a = new Dog(); // 使用子类创建对象 } }
1.2 修饰方法
在父类中,可能存在一些方法,必须要在子类中重写,那么可以在该方法前面加上abstract关键字,指定该方法在子类中应该重写。
public abstract class Animal { private String name; // 只需要方法的声明,不需要实现,所以没有大括号,直接使用分号结束 public abstract void eat(); }
public class Dog extends Animal{ @Override public void eat() { System.out.println("狗在吃..."); } }
一个抽象类中可以没有抽象方法
有抽象方法的类必然是抽象类
抽象方法不能使用private,没有意义
当子类继承一个有抽象方法的抽象类时,会报错。此时有两种解决方案:
将子类也设置为抽象类
实现(重写)父类中所有的抽象方法
二、final
final,最终的,最后的,终结的,不可更改的。
2.1 修饰类
表示该类不能被继承。
注意:final不能和abstract一起使用。
2.2 修饰方法
表示该方法不能被重写。
2.3 修饰变量
表示该变量一旦赋值,不能改变,称为常量。
2.3.1 修饰属性
该属性必须在创建对象后,要有值。赋值的时机:
直接在属性定义时赋值
public class Dog{ private final String name = ""; }
在代码块中赋值
public class Dog{ private final String name; { name = ""; } }
在构造方法中赋值
public class Dog{ private final String name; public Dog() { this.name = ""; } public Dog(String name) { this.name = name; } }注意:不能再次赋值,所以当在代码块中赋值后,不能在构造方法中赋值,因为代码块在构造方法前执行。
2.3.2 修饰局部变量
public class Dog extends Animal{ public void m1() { // 先声明,后赋值 final int n; n = 6; // n = 5; 会报错,不能重复赋值 // 声明的同时赋值 final int m = 3; } }
2.3.3 修饰方法的参数
public class Dog extends Animal{ public void m2(final int n) { // n = 5; 会报错, 表示该值不能修改 } }
2.3.4 常量
在类中一般会定义一些变量,声明为final修饰,视作常量。
在项目中,固定使用的一些常数,不希望在使用过程中被改变,会定义成常量。
常量定义的规范:单词全大写,多个单词使用下划线隔开。
// Integer类中的整数最大值: public static final int MAX_VALUE = 0x7fffffff; // Math类中的PI的值: public static final double PI = 3.14159265358979323846;
三、static关键字
3.1 修饰属性
表示该属性为静态属性,也叫类属性。
同一个类共享同一个类型属性空间,也可以使用对象操作。但是不推荐。应该使用类名访问。
创建的对象在堆中,而类属性在方法区。
注意:不能修饰局部变量。不需要创建对象就可以使用类属性。
public class Student { public String name; public int age; public static String className; public void introduce() { // static int n = 5; // 报错,不能修饰局部变量 System.out.println(className); } }
public class Demo1 { public static void main(String[] args) { Student s1 = new Student(); s1.name = "张三"; s1.age = 20; s1.className = "0班"; // 可以操作,但是不推荐 Student.className = "1班"; // 静态属性,类属性 Student s2 = new Student(); s2.name = "李四"; s2.age = 18; Student.className = "2班"; System.out.println(s1.name + "," + s1.age + "," + Student.className + "---" + s2.name + "," + s2.age + "," + Student.className); } }
在实例方法中是否可以调用静态属性?
可以,因为静态属性不需要创建对象就可以访问。创建了对象也可以访问。
3.2 修饰方法
表示该方法为静态方法(类方法)。
即直接使用类名调用的方法,不需要创建对象即可调用。
public class Student { public String name; public int age; public static String className; public static int count = 0; public Student() { count++; System.out.println("对象被创建了"+count+"次"); } public void introduce() { // static int n = 5; // 报错,不能修饰局部变量 System.out.println(className); } // 静态方法,类方法 public static void m1() { System.out.println("m1===被调用"); } }
静态方法不需要创建对象即可使用,直接用类名访问。
在静态方法中可以调用其他静态方法,但是不能调用非静态方法(实例方法)。
静态方法中可以调用静态属性,但是不能调用非静态属性(实例属性)。
静态方法中不能使用this和super。
静态方法可以继承,不能重写,没有多态。
3.3 修饰代码块
动态代码块:在类中直接使用一对大括号中的代码。创建对象时执行。
执行顺序:
初始化属性
执行动态代码块
执行构造方法
public class A { public String name = "aaa"; { System.out.println(name); System.out.println("动态代码块"); } public A() { System.out.println("构造方法"); } } public class Demo2 { public static void main(String[] args) { new A(); } }
静态代码块:使用static修饰的代码块叫静态代码块。类加载时执行。而且只执行一次。
创建对象时会先加载类。
加载类:将类的信息加载到内存中的方法区,以便得到对象所需的空间大小,方便创建对象。
执行顺序:
静态属性初始化(仅一次)
静态代码块(仅一次)
实例属性
动态代码块
构造方法
public class A { public static String sname = "静态属性"; public String name = "实例属性"; { System.out.println(name); System.out.println("动态代码块"); } public A() { System.out.println("构造方法"); } static { System.out.println(sname); // System.out.println(name); // 不能访问实例属性 System.out.println("静态代码块"); } } public class Demo2 { public static void main(String[] args) throws ClassNotFoundException { // new A(); // 加载类 // Class.forName("com.qf.day13.A"); new A(); // 会先加载类 // new A(); } }
3.4 继承时的执行顺序
经典面试题:执行顺序:
父类静态属性(仅一次) 父类静态代码块(仅一次) 子类静态属性(仅一次) 子类静态代码块(仅一次)
父类实例属性 父类动态代码块 父类构造方法 子类实例属性 子类动态代码块 子类构造方法
public class A { public static String sname = "父类静态属性"; public String name = "父类实例属性"; { System.out.println(name); System.out.println("父类动态代码块"); } public A() { System.out.println("父类构造方法"); } static { System.out.println(sname); System.out.println("父类静态代码块"); } } public class B extends A{ public static String sname1 = "子类静态属性"; public String name1 = "子类实例属性"; { System.out.println(name1); System.out.println("子类动态代码块"); } public B() { System.out.println("子类构造方法"); } static { System.out.println(sname1); System.out.println("子类静态代码块"); } } public class Demo2 { public static void main(String[] args){ new B(); } }