三个修饰符
一、abstract
1.1 abstract修饰类
抽象的。
- 因为父类不应该直接创建对象,但是创建也不会提升错误,为了限制这种错误行为的发生,可以给类加一个修饰符abstract,表示该类不能创建对象。
// 限制类的对象创建
public abstract class Animal {
}
public class Dog extends Animal{
}
public class TestMain {
public static void main(String[] args) {
// Animal animal = new Animal(); //会报错,因为abstract修饰类时不能创建对象
Animal animal = new Dog();
}
}
1.2 abstract修饰方法
- 父类的方法,如果每个子类都会重写该方法,意味着该方法在父类中的实现根本不会被调用,此时可以删掉父类的方法实现,只留下方法的声明,并加上abstract关键字,表示该方法为抽象方法。
一个抽象类中可以没有抽象方法。
但是有抽象方法的类必须是抽象类。
在继承一个抽象类时,如果该类中有抽象方法,其子类要么也定义成抽象类,要么实现所有的抽象方法。
public abstract class Animal {
public abstract void eat();
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
二、static
2.1 概念
静态的。类的。
2.2 修饰属性
类属性,该类所有的对象共享的属性。不需要创建对象就可以访问,应该使用类名来访问类属性。
注意:实例属性是对象创建时才分配空间,每个对象都有独立的空间。类属性是整个类共享的一个空间,在方法区。所以所有的对象对该属性进行操作会相互影响。应该使用类名操作。
public class Student {
public String name; // 实例属性,每个对象特有
public static String classes; // 类属性,对象共享一个属性
}
public class TestMain {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "张三";
Student.classes = "一班";
Student student2 = new Student();
student2.name = "李四";
Student.classes = "二班"; // 对类属性进行修改,会影响所有的对象
Student student3 = new Student();
System.out.println(student1.name);
System.out.println(Student.classes);
System.out.println(student2.name);
System.out.println(Student.classes);
System.out.println(student3.name);
System.out.println(Student.classes);
}
}
2.3 修饰方法
当static修饰方法时,直接通过类名就可以访问该方法,不需要创建对象。
public class Student {
public static void m1() {
}
}
public class TestMain {
public static void main(String[] args) {
Student.m1();
}
}
2.4 类方法中this和super的要求
在类方法中,只能访问类属性和其他类方法,不能访问与实例属性和实例方法,也不能使用this和super
public class Student {
public String name; // 实例属性,每个对象特有
public static String classes; // 类属性,对象共享一个属性
public void say() {
System.out.println(name);
}
public static void m1() {
// 在类方法中,只能访问类属性和其他类方法,不能访问与实例属性和实例方法,也不能使用this和super
System.out.println(classes);
// System.out.println(name); // name需要对象才能访问
m2();
// say(); // say方法需要对象才能访问
// System.out.println(this.name); // this表示当前对象
// System.out.println(super.toString()); // super表示父类对象
}
public static void m2() {
}
}
2.5 修饰代码块
代码块,是指在类中定义的一段代码(不在方法中)。
直接定义的代码叫做动态代码块,使用static定义的代码叫做静态代码块。
public class Employee {
{
System.out.println("动态代码块");
}
static {
System.out.println("静态代码块");
}
}
代码块一般用来初始化属性。动态代码块在创建对象时执行,静态代码块在加载类时执行。
public class Employee {
public static int count;
public String name;
// 在创建对象时执行,一般用来给属性赋值
{
Random random = new Random();
int num = random.nextInt(100);
if(num % 2 == 0) {
name = "张三";
}else {
name = "李四";
}
System.out.println("动态代码块");
}
// 在加载类时执行,一般用来给类属性赋值,或者加载系统资源
static {
Random random = new Random();
count = random.nextInt(100);
System.out.println("静态代码块");
}
}
注意:静态代码块在类加载时执行,仅执行一次(只会加载一次),动态代码块每次创建对象时都会执行。
2.6 对象创建过程中的顺序
过程分析:
加载父类,给父类的静态属性赋值,并执行父类的静态代码块。
加载子类,给子类的静态属性赋值,并执行子类的静态代码块。
父类的对象操作
- 给属性赋值
- 执行动态代码块
- 执行构造方法
子类的对象操作
- 给属性赋值
- 执行动态代码块
- 执行构造方法
public class Animal {
public String a1 = "Animal内的实例属性";
public static String b1 = "Animal内的静态属性";
public Animal() {
System.out.println("Animal开始创建对象");
}
{
System.out.println(a1);
System.out.println("Animal内的动态代码块");
}
static {
System.out.println(b1);
System.out.println("Animal内的静态代码块");
}
}
public class Dog extends Animal{
public String d1 = "Dog内的实例属性";
public static String c1 = "Dog内的静态属性";
public Dog() {
System.out.println("Dog开始创建对象");
}
{
System.out.println(d1);
System.out.println("Dog内的动态代码块");
}
static {
System.out.println(c1);
System.out.println("Dog内的静态代码块");
}
}
public class TestDog {
public static void main(String[] args) {
new Dog();
}
}
运行结果:
Animal内的静态属性
Animal内的静态代码块
Dog内的静态属性
Dog内的静态代码块
Animal内的实例属性
Animal内的动态代码块
Animal开始创建对象
Dog内的实例属性
Dog内的动态代码块
Dog开始创建对象
三、final
final即最终的。
3.1 修饰类
表示该类不可被继承。
// 表示类不能被继承
public final class Father {
}
// 下面的代码会报错
public class Son extends Father{
}
3.2 修饰方法
表示该方法不可被重写。
public class Father {
public final void m1() {
}
}
public class Son extends Father{
// 以下代码会报错,因为final的方法不能被重写
@Override
public void m1() {
System.out.println("son");
}
}
final修饰方法时能否与abstract一起使用?
final修饰类时能否与abstract一起使用?
3.3 修饰属性、变量
表示该属性或变量不能再次赋值(修改)。
注意:如果一个实例属性使用了final关键字,必须在创建完对象后就有值,所以可以在属性定义时直接赋值,也可以在构造方法中赋值,也可以在动态代码块中赋值,但是赋值后就不能改变。
public class Son extends Father{
public final String name; // 可以直接赋值public final String name = "张三";
public Son() {
name = "李四"; // 也可在构造方法中赋值
}
//{
// name = "李四"; // 也可以在动态代码块中赋值
//}
}
如果静态属性是final的,需要在加载类后有值,可以在声明时赋值,也可以在静态代码块中赋值。但是赋值后就不能改变。
public class Father {
public final static int count; // 可以直接赋值public final static int count = 5;
static {
count = 10; // 也可以在静态代码块块中赋值
}
}
修饰普通变量,只要在使用前赋值即可,但是赋值后不能改变。
public class Father {
public void m1() {
final int n;
n = 5;
// n = 10; // 会报错
}
}
注意:由于final修饰的变量或属性都具备有赋值后就不能改变的特征,所以统称为常量。常量的定义规则是全大写,单词之间用下划线拼接。
public class MyClass{
public static final int PAGE_SIZE = 10; // 页面大小
}
注意:当final修饰的变量引用一个对象时,是指变量中保存的对象的地址不能改变,而不是对象的属性值不能变,如果是数组,是指数组的地址不能变,不是数组的值不能变。
public class TestMain {
public static void main(String[] args) {
// arr中值?是数组分配空间对应的地址,所以是指地址不能变
final int [] arr = new int[5];
arr[0] = 5; // 而数组中成员赋值是改变的变量的值,而不是数组的地址
arr[0] = 6;
// arr = new int[10]; // 此行代码有错,地址不能改变
// dog中的值是指Dog对象的地址,所以是指地址不能变
final Dog dog = new Dog();
dog.age = 1;// 而给属性赋值只是改变的该对象的属性值,地址没有改变
dog.age = 2;
// dog = new Dog();// 此行代码有错,地址不能改变
}
// 数组扩容
public void m1(final int [] arr) {
// arr = new int[arr.length + 1]; // 此行代码报错,原因是final修饰的引用变量中保存的地址不能改变
}
}