Java--三个修饰符(abstract、static、final)
1.第一个修饰符:abstract(抽象)
拿动物类来说,现实生活中的动物都是具体的,比如说:猫、狗、狮子、老虎等 这些都是‘动物类’的子类,并没有“动物”这个对象,它在现实生活中不存在,所以不应该被new() 实例化成对象,这个时候呢,需要把这个动物类定义成抽象类,来限制这种对象的创建(public abstract class Animal{})。
抽象类:被abstract 修饰的类,称为抽象类。抽象类意为不够完整的类、不够具体的类,抽象类对象无法独立存在,即不能new对象。
作用:1:可被子类继承,提供共性属性和方法 2:可声明为引用,更自然的使用多态。
经验:抽象父类,可作为子类的组成部分,依附于子类对象存在,由父类共性+子类独有组成完整的子类对象。
抽象方法:被abstract修饰的方法,称为抽象方法。只有方法声明,没有方法实现({}的部分)。意为不完整的方法,必须包含在抽象类中。
总结:抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类。
子类继承抽象类后,必须重写父类中所有的抽象方法,否则子类还是抽象类。
示例:
------------------------------------------------------------------------- package com.monv.abs; /** * 交通工具类 * @author lenovo * */ public abstract class Vehicle { private String Brand; public Vehicle() { } public Vehicle(String brand) { super(); this.Brand = brand; } public String getBrand() { return Brand; } public void setBrand(String brand) { this.Brand = brand; } //前进的方法 这个方法在子类中有被重写 所以这里的代码部分就没有用了 可以优化成抽象方法 public abstract void run(); } ----------------------------------------------- package com.monv.abs; public class Car extends Vehicle{ public Car() { // TODO Auto-generated constructor stub } public Car(String brand) { super(brand); } //重写父类中的构造方法 @Override public void run() { System.out.println(super.getBrand()+"牌的汽车正在前进。。。"); } } ----------------------------------- package com.monv.abs; public class Bike extends Vehicle{ public Bike() { } public Bike(String brand) { super(brand); } //重写父类中的方法 @Override public void run() { System.out.println(super.getBrand()+"牌的自行车正在前进。。。"); } } ------------------------ package com.monv.abs; /** * 主人类 * @author lenovo * */ public class Master { private String name; public Master() { // TODO Auto-generated constructor stub } public Master(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } //回家的方法 public void goHome(Vehicle vehicle) { System.out.println(this.name+"下班回家了"); vehicle.run(); } } -----------------测试------------------------ package com.monv.abs; public class TestMaster { public static void main(String[] args) { Master xiaoming = new Master("小明"); Vehicle car = new Car("宝马"); Vehicle bike = new Bike("永久"); xiaoming.goHome(car); xiaoming.goHome(bike); } } -------------------------------------------------------------------- 结果: 小明下班回家了 宝马牌的汽车正在前进。。。 小明下班回家了 永久牌的自行车正在前进。。。 --------------------------------------------------------------------
2.第二个修饰符:static(静态)
用 static 修饰的属性和方法称为静态属性(类属性)、静态方法(类方法) ;
他们是属于整个类的,是全类所有对象共享的成员,在全类中只有一份,不因创建多个对象而产生多份;不必创建对象,可以直接通过类名访问。
实例属性是每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象
静态属性是整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
静态属性
调用:类名.静态属性
package com.monv.static_01; public class Student { String name;//实例属性 int age; //学生数量 单个学生不适合有学生数量这个属性 ,这个属性是属于整个类(学生类)的 所以要定义为静态的 static int count; public void showage() { System.out.println("姓名:"+this.name+"--年龄:"+this.age); } } ---------------------------------------------- package com.monv.static_01; public class TestStudent { public static void main(String[] args) { Student s1 = new Student(); s1.name="小明"; s1.age = 22; Student s2 = new Student(); s2.name = "小红"; s2.age = 23; s1.showage(); s2.showage(); //------------静态属性的调用-类名.静态属性(Student.count)----------- Student.count = 50;//在整个内存中只有一份 System.out.println("学生数量为:"+Student.count); } } ------------------------------------------------------------------
案例:统计一个类的对象被创建了多少次?
package com.monv.static_01; public class Teacher { String name;//姓名 int age; //年龄 double salary;//工资 //保存对象创建的次数 static int count = 0; //创建对象的时候 会执行默认的构造方法 所以在默认构造方法中给count+1 public Teacher() { Teacher.count++; } public void show() { System.out.println("姓名:"+name+" 年龄:"+age+" 工资:"+salary); } } ---------------------------------------------- package com.monv.static_01; public class TestTeacher { public static void main(String[] args) { System.out.println("对象创建之前的次数:"+Teacher.count); Teacher t1 = new Teacher(); Teacher t2 = new Teacher(); Teacher t3 = new Teacher(); System.out.println("对象创建之后的次数:"+Teacher.count); } } ------------------------------------------------------- 对象创建之前的次数:0 对象创建之后的次数:3 -------------------------------------------------------
静态方法
调用:类名.静态方法
- 静态方法可以直接访问静态成员的
- 静态方法不能直接访问非静态成员。(静态方法属于类,不需要创建对象就可以用,而非静态成员属于每个对象,在使用静态方法的时候没有对象,无法访问非静态成员)
- 静态方法中不允许使用this或super关键字。(this调用的是本类的属性或方法,调用的也是非静态。super是父类的属性和方法,也是非静态的)
- 静态方法可以继承,不能重写,没有多态。
静态代码块
语法:static{}
什么时候执行静态代码块? 在类加载时,触发静态代码块的执行(仅一次)。执行地位:静态属性初始化后。
作用:可为静态属性赋值,或必要的初始行为。
package com.monv.static_01; public class Person { String name; //静态成员 人的最大数量 static int max = 0; //静态代码块:类加载的时候则执行静态代码块,而且仅执行一次,可以进行一些初始化操作 static { max = 10000; System.out.println("人的最大数量:"+max); } //静态方法 public static void method() { } } -------------------------------------------------------------------- package com.monv.static_01; public class TestPerson { public static void main(String[] args) { Person p;//用Person 创建一个对象p 这种静态方法没有执行(只用类创建了变量,类是不会加载的) Person p1 = new Person();//在new实例化对象的时候执行静态代码块 Person p2 = new Person();//在new实例化对象的时候执行静态代码块 Person.method();//调用静态方法也触发静态代码块的执行 } } -----------------------------------------------------------------------
类加载
- JVM首次使用某个类时,需通过CLASSPATH查找该类的 .class 文件
- 将.class文件中对类的描述信息加载到内存中,进行保存。(从硬盘上加载到内存中,在加载的时候会触发静态代码块的执行)
如:包名,类名,父类,属性,方法,构造方法。。。。(把这些加载到内存中)
- 加载时机(以下都可以触发类的加载,类加载的时候会触发静态代码块的执行):
- 创建对象
- 创建子类对象
- 访问静态属性
- 调用静态方法
- 主动加载:Class.forName("全限定名")
总结:
- static修饰的成员为静态成员、无需创建对象,可以直接通过类名访问
- 静态方法不能直接访问非静态成员
- 静态方法中不能使用this或super。
- 静态方法可以继承、不能重写、没有多态
- 静态代码块在类加载时被执行,且只执行一次
3.第三个修饰符:final(最终)
- 概念:最后的,不可更改的。
- final可以修饰的内容:类(最终类) 不能被继承;方法(最终方法)不能被覆盖重写,可以使用;变量(最终变量) 常量不能被更改
final修饰方法:
-------------------汽车类父类------------------------ package com.monv.final_1; /** * final修饰类:表示最终类 不能被继承 * @author lenovo * */ public class Car { String brand; String color; //最终方法,子类不能被重写,但是可以被继承 public final void run() { System.out.println("汽车在前进..."); } } --------------------小汽车类子类----------------- package com.monv.final_1; public class SmallCar extends Car{ //父类中run方法被final修饰 是最终方法不能重写 但是可以使用 // @Override // public void run() { // System.out.println("小汽车正在前进..."); // } } -----------------------测试------------------------ package com.monv.final_1; public class TestCar { public static void main(String[] args) { SmallCar c1 = new SmallCar(); c1.run();//父类中的方法 } } ----------------------------------------------------
final修饰变量:此变量值不能被改变(常量) 所有final修饰的变量只能赋值一次,值不允许改变。
- 修饰局部变量-----变为常量
public class Car { String brand; String color; //最终方法,子类不能被重写,但是可以被继承 public final void run() { System.out.println("汽车在前进..."); final int num = 100;//被final修饰 变为常量 num的值就不能改变 // num = 200; num变为了常量 就不能再改变了 这句话就会报错 } }
- 修饰实例变量----变成实例常量
实例常量不再提供默认值,必须手动赋予初始值。
赋值时机:显示初始化(直接在定义的时候赋值)、构造方法(在构造方法中对其赋值)。
注意:如果在构造方法中为实例常量赋值,必须保证所有的构造方法都能对其正确赋值。
public class Car { // final String brand = "宝马";//brand被final修饰变成实例常量 编译器不再给默认值 必须赋值,并且只能赋值一次 final String brand ;//也可以在构造方法中给实例常量赋值 String color; public Car() { this.brand = "宝马"; } //多个构造方法 也必须要给实例常量赋值 public Car(String brand) { this.brand = brand; } //最终方法,子类不能被重写,但是可以被继承 public final void run() { System.out.println("汽车在前进..."); final int num = 100;//被final修饰 变为常量 num的值就不能改变 // num = 200; num变为了常量 就不能再改变了 这句话就会报错 // brand = "大众";//再次赋值就会报错 } }
- 修饰静态变量---静态常量
静态常量不再提供默认值,必须手动赋予初始值。
赋值时机:显示初始化(直接在定义的时候赋值)、静态代码块。
public class Car { //静态变量被final修饰变成静态常量 编译器不再给默认值 必须赋值,并且只能赋值一次 final static String ADDRESS="上海";//定义的时候直接赋值 final static String ADDRESS1; //静态代码块赋值 static { ADDRESS1 = "北京"; } }
- 对象常量
final 修饰基本类型:值不可以被改变
final 修饰引用类型:地址不可以被改变
package com.monv.final_1; import java.util.Arrays; public class TestStudent { public static void main(String[] args) { //final 修饰基本类型 final int num = 20; // num = 30;//报错 不能被修改 //final 修饰引用类型 final int[] nums = new int [] {10,20,30}; // nums = new int[5];//不能对nums在进行初始化了 会报错 nums[2] = 40;//但是可以修改nums中元素的值 System.out.println(Arrays.toString(nums)); final Student s1 = new Student(); // s1 = new Student(); s1变成常量了 就不能再对其赋值 地址不可以改变 s1.name = "小明";//但是可以对s1的属性进行赋值 System.out.println(s1.name); } }
总结:
- final修饰类:此类不能被继承
- final修饰方法:此方法不能被覆盖
- final修饰变量:此变量不能被改变(无初始值,只允许赋值一次)
局部常量:显示初始化(定义的时候直接赋值)
实例常量:显示初始化、构造方法赋值
静态常量:显示初始化、静态代码块
基本类型常量:值不可变。
引用类型常量:地址不可变。引用类型的数量可以改变。