深入理解Java中的三个修饰符(抽象(abstract)、静态(static)和最终的,不可变(final))【配视频】

简介: 🍅程序员小王的博客:程序员小王的博客🍅 欢迎点赞 👍 收藏 ⭐留言 📝🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕🍅java自学的学习路线:java自学的学习路线

一、abstract

抽象的配套视频


深入理解Java抽象类

概念:

abstract (抽象的)
可以修饰类、方法,使程序设计的更加合理

例:Shape类不合理的设计

class Shape{//图形类
  public double area(){//计算面积
    return 0.0;//为了满足语法,给出无意义的实现
  }
  public  double girth(){//计算周长
    return 0.0;//为了满足语法,给出无意义的实现
  }
}


例:Animal类不合理的设计

class Animal{
  String name;
  int age;
  public Animal(){}
  public Animal(String name, int age){
    this.name = name;
    this.age = age;
  }
  public void eat(){ }//无法描述Animal的吃行为
  public void sleep(){ }//无法描述Animal的吃行为
}


这些不合理的方法不能移除,因为如果父类中没有声明则影响多态的使用(参考多态特点第二条)


(一)抽象类

概念:被abstract修饰的类称为抽象类

例:

abstract class MyClass{
}


特点:抽象类只能声明引用不能创建对象

例:


abstract class MyClass{
}
MyClass mc; //
mc = new MyClass();//Error 不能创建对象


(二)抽象方法

概念:被abstract修饰的方法,称之为抽象方法

例:

abstract void m1();


特点:


1.抽象方法只能有声明,不能有实现

例:


public abstract void m1();//抽象方法
public abstract void m2();//抽象方法
public void m3(){//非抽象方法
   //方法的实现
}


2.抽象方法只能定义在抽象类中

例:


abstract class MyClass{//抽象类
    public abstract void m1();//抽象方法
}


3.当父类存在抽象方法时,子类必须覆盖父类的抽象方法,否则子类还是抽象类

例:子类没有覆盖父类的抽象方法


abstract class Super{//抽象类
  public abstract void m1();//抽象方法
}
abstract class Sub extends Super{//因为没有覆盖MyClass中的抽象方法,所以子类必须也是抽象类
}


例:子类覆盖父类的抽象方法


abstract class Super{//抽象类
    public abstract void m1();//抽象方法
}
class Sub extends Super{
    public void m1(){//覆盖父类中的抽象方法 添加实现{  }
    }
}


 子类覆盖父类的抽象方法,并添加方法的实现{ },这种特殊的覆盖语法称之为实现。

 注意:由于继承关系可以多级继承,当继承到多个抽象方法时,子类如果不想成为抽象类,必须都予以实现。


(三)应用案例

使用抽象类、抽象方法将父类设计的更加合理,抛弃无意义方法实现,保留声明部分,既不影响多态的使用,也不影响原有继承体系

例:Shape类


abstract class Shape{//图形类
  public abstract double area();//计算面积  
  public  abstract double girth()//计算周长
}


例:Animal类

abstract class Animal{
    String name;
    int age;
    public Animal(){}
    public Animal(String name, int age){
      this.name = name;
      this.age = age;
    }
    public abstract void eat();//无法描述Animal的吃行为
    public abstract void sleep();//无法描述Animal的吃行为
}


二、static

静态和final配套视频


static和final关键字可以用来修饰什么,分别起什么作用

概念

static(静态)
可以修饰类、属性、方法、动态初始化代码块、import


(一)静态属性(类变量)

概念:被static修饰的属性,称之为静态属性或类变量


特点:静态属性与对象无关,有独立的存储区域,可以通过类名直接访问,可以被所有对象共享

例:



class MyClass{
    int a;//一般属性
    static int b;//静态属性
}
//为静态属性赋值
MyClass.a = 10;
//静态属性被所有对象共享
MyClass mc1 = new MyClass()
System.out.println( mc1.a );//10
MyClass mc2 = new MyClass();
System.out.println( mc2.a );//10


 静态属性虽然可以通过引用名.的方式使用,但是极度不推荐,容易与一般属性造成混淆


(二)静态方法

概念:被static修饰的方法称为 静态方法


特点:


1.静态方法与对象无关,可以使用类名直接调用

例:

class Super{
  //静态方法
  public static void m1(){
  }
}
//调用Super类中的静态方法
Super.m1();


2.静态方法与对象无关,因此不能使用非静态的成员(成员变量、成员方法),因为它们需要创建对象后才能使用。

例:

class Super{
  int a; //一般属性
  public void m1(){ }//一般方法
  public static void m2(){//静态方法
    System.out.println( a );//错误:静态方法中不能访问非静态成员
    m1();//错误:静态方法中不能访问非静态成员
  }
}


 静态方法由于与对象无关,因此也不能在静态方法中使用this或super关键字

 思考:为什么在学函数的时候,所有的函数都要加static关键字?


3.静态方法可以被继承

例:

class Super{
  public static void m1(){
  }
}
class Sub extends Super{ }
//调用父类中的m1方法
Sub.m1();//可以从父类中继承静态方法m1();


4.静态方法只能被静态方法所覆盖

例:

class Super{
  public static void m1(){
  }
}
class Sub extends Super{ 
  //覆盖父类中的静态方法
  public static void m1(){
  }
}
Sub.m1();//调用覆盖后的m1方法


5.静态方法在使用时没有多态

例:

class Super{
  public static void m1(){}
}
class Sub extends Super{
  public static void m1(){}
}
Super sup = new Sub();//多态语法
sup.m1();//编译后---->Super.m1();引用名替换为引用类型


小结:

static 修饰属性:静态属性,所有此类对象共享,可以直接通过类名访问
static 修饰方法:静态方法,可以直接通过类名方法,静态方法与对象无关,不能使用非静态属性或方


(三)类加载

概念:JVM将硬盘中的.class文件加载到内存中的一种行为



类加载的时机与过程


1. 当虚拟机第一次遇到某个类时(new对象、调用静态成员、使用子类)某个类时,需要通过CLASSPATH查找该类的 .class 文件。
2. 将.class文件中保存的类的描述信息(属性、方法..等等)加载到内存中进行保存。
3. 加载时会初始化该类的必要信息,初始化静态属性、静态方法,调用静态初始化代码块等。
4. 类加载只会执行一次。


例:加载时机

class MyClass{
   static int a = 10;
   //静态初始化代码块,类加载时执行
   static{
      System.out.println("类加载...");
   }
}
class Sub extends MyClass{
}
//加载时机 1:访问静态成员
MyClass.a; 
//加载时机 2:创建对象
MyClass mc = new MyClass();
//加载时机 3:创建子类对象或加载子类时
Sub.a;
Sub s = new Sub();



 注意:只声明引用,不会进行类加载。


(四)静态初始化代码块

概念:使用static修饰的动态初始化代码块 *,称为静态初始化代码块


特点:在类加载时执行一次


作用:用于初始化静态属性,执行在整个系统中只需要运行一次的代码(加载资源、构建容器等)

例:


class MyClass{
    static int a;
    static{//静态初始化代码块
        a = 10;
    }
}


(五)类加载的过程

过程


1. 如果有需要先加载父类
2. 初始化静态属性
3. 按照顺序初始化静态属性,或执行静态代码块


验证例:

class Super{
    static{
        System.out.println("static in Super");
    }
    public Super(){
        System.out.println("init Super");
    }
}
class Sub extends Super{
    static{
        System.out.println("static in Sub");
    }
    public Sub(){
        System.out.println("init Sub");
    }
}


第一次创建子类对象时输出:

static in Super
static in Sub
init Super
init Sub


(六)静态引入

使用import static 引入类中的静态资源

例:引入java.lang.System中的静态属性out

//静态引入
import static java.lang.System.out;
public class Test {
    public static void main(String[] args) {
         //使用System.out进行输出时,不必再加入类名
         out.print("Hello");
    }
}

(七)static小结

static 修饰的属性为静态属性(类变量)每个对象共用一份,可以使用 类名.属性名的方式直接访问

static 修饰的方法为静态方法(类方法)每个对象共用一份,可以使用 类名.属性名的方式直接访问

static 修饰的初始化代码框为静态初始化代码框,主要用于在类加载时对静态变量进行初始化工作

import static 称为静态引入可以`引入静态资源,使用时不必明确类名

三、final

概念:

final (最终的)
可以修饰 变量(局部变量、属性)、方法、类

(一)常量

概念:被final修饰的变量称为常量


修饰局部变量特点:只能赋值一次,不可修改

例:


final int a = 10;//只能赋值一次
a = 10;//编译错误
final int[] as = new int[5];//只能赋值一次
as = new int[10];//编译错误

修饰成员变量特点:值不可遍,必须手动初始化

例:

class MyClass{
  final int a;//编译错误
}


初始化方式

  1.直接初始化

例:

class MyClass{
  final int a = 10;
}


 由于被final修饰的属性不可变,所以为了避免内存空间的浪费,该属性还应该被static修饰


  2.初始化代码块| 静态块初始化代码块

例:


class MyClass{
    final int a;
    {
      a = 10;
    }
}


  3.构造方法赋值

例:

class MyClass{
    final int a;
    public MyClass(){
        a = 10;
    }
    public MyClass(int a){
        this.a = a;
    }
}


 注意:由于final修饰的属性必须保证能够被初始化,所以每个构造方法都要为属性初始化


(二)最终方法

概念:被final修饰的方法,称为最终方法


特点:此方法不能被子类覆盖

例:


class Super{
    public final void m1(){
    }
}
class Sub extends Super{
  //编译错误:父类final修饰的方法不能被覆盖
   public void m1(){
   }
}

(三)最终类

概念:被final修饰的类,称为最终类


特点:不能被继承

例:

final class Super{
}
//编译错误:父类为最终类,不能继承
class Sub extends Super{
}


(四)修饰符组合

修饰符可以组合使用,请遵循一下规则:

1. static、final与abstract不能连用,因为abstract为抽象的,修饰类必须由子类继承才能使用,修饰方法必须由子类覆盖才能使用,不能调用abstract修饰的方法等特点,与static final 意义冲突。
2. private 与 abstract不能一起修饰方法,否则该方法将无法被覆盖
3. static与final可以连用,意义不冲突
4. 在定义常量时通常使用public static final 进行声明,称为公开静态常量

五、三个修饰符答案

客观题

1.关于三个修饰符描述不正确的是( a d )


a. static 可以修饰所有类型的变量


b. static 可以修饰方法


c. final 可以修饰类


d. abstract 可以修饰类、方法、属性


2.下面关于 static 描述不正确的是( c d )


a. static 修饰的属性称为静态变量,被所有的对象共享


b. static 修饰的方法,可以直接用 类名.方法名(参数)调用


c. static 修饰的方法中可以直接访问本类的其他方法


d. 父类中定义了 static 修饰的方法,子类中不能再定义同名的 static 方法


3.关于 final 描述错误的是( a b )


a. final 修饰的类是作用范围内常量,只允许一次赋值


b. final 修饰的方法不能被继承


c. final 修饰的方法不允许被覆盖


d. final 修饰的类没有子类


4.关于 abstract,以下选项正确的是( a b c d )


a. abstract 类中可以没有 abstract 方法


b. abstract 类的子类也可以是 abstract 类


c. abstract 类不能创建对象,但可以声明引用


d. abstract 方法不能有方法体


5.仔细阅读以下程序,写出程序输出的结果。

class Myclass{
    static int a;
    int b;
}
public class TestMain{
    public static void main(String[] args){
        MyClass mc1 = new MyClass();
        MyClass mc2 = new MyClass();
        mc1.a=100;
        mc1.b=200;
        mc2.a=300;
        mc2.b=400;
        System.out.println(mc1.a);
        System.out.println(mc1.b);
        System.out.println(mc2.a);
        System.out.println(mc2.b);
    }
}


运行结果为:
300
200
300
400


6.仔细阅读以下程序,选出哪些代码会导致程序出错( e g )

class MyClass{
    int a;
    static int b;
    void fa(){}
    static void fb(){}
    public void m1(){
        System.out.println(a);//1
        System.out.println(b);//2
        fa();//3
        fb();//4
    }
    public static void m2(){
        System.out.println(a);//5
        System.out.println(b);//6
        fa();//7
        fb();//8
    }
}


a. //1 e. //5


b. //2 f. //6


c. //3 g. //7


d. //4 h. //8


7.仔细阅读以下程序,写出程序运行输出的结果。

class MyClass{
    static int i = 10;
    static{
        i = 20;
        System.out.println("In Static");
    }
    public MyClass(){
        System.out.println("MyClass()");
    }
    public MyClass(int i){
        System.out.println("MyClass(int)");
        this.i = i;
    }
}
public class TestMain{
    public static void main(String[] args){
        MyClass mc1 = new MyClass();
        System.out.println(mc1.i);
        MyClass mc2 = new MyClass(10);
        System.out.println(mc2.i);
    }
}


运行结果为:
In Static
MyClass()
20
MyClass(int)
10


8.仔细阅读以下代码,关于代码描述正确的是( c )


class MyClass{
    final int value;
    public MyClass(){}
    public MyClass(int value){
        this.value = value;
    }
}
public class TestMain{
    public static void main(String[] args){
        MyClass mc = new MyClass(10);
        System.out.println(mc.value);
    }
}


a. 编译通过,输出 10


b. 编译不通过,把第 2 行改为 final int value = 10;


c. 编译不通过,把第 3 行改为 public MyClass(){ value = 10; }


d. 以上描述都不正确


9.仔细阅读以下代码,关于以下程序描述正确的是( c )

class MyValue{
    int value;
}
public class TestFinal{
    public static void main(String[] args){
        final MyValue mv = new MyValue();
        mv.value = 100;
        //1
        System.out.println(mv.value);
    }
}


a. 编译不通过


b. 编译通过;在//1 处加上:mv.value = 200; 则编译不通过


c. 编译通过。如果在//1 处加上:mv = new MyValue(); 则编译不通过


d. 以上描述都不正确


10.仔细阅读以下代码,选出正确选项( a )


class MyClass{
    public void printValue(final int value){
        System.out.println(value);
    }
    public void changeValue(int value){
        value = value*2;
        System.out.println(value);
    }
}
public class TeatMain{
    public static void main(String args[]){
        MyClass mc = new MyClass();
        int value = 5;
        final int fvalue = 10;
        mc.printValue(value);//1
        mc.printValue(fvalue);//2
        mc.changeValue(value);//3
        mc.changeValue(fvalue);//4
    }
}

a. 编译通过


b. //1 出错


c. //2 出错


d. //3 出错


e. //4 出错


11.仔细阅读以下代码,代码中哪些内容是错误的( b c )

abstract class MyAbstractClass{
    public abstract void m1();//1
    abstract protected void m2(){}//2
}
class MySubClass extends MyAbstractClass{
    void m1(){}//3
    protected void m2(){}//4
}

a. //1


b. //2


c. //3


d. //4


12.仔细阅读以下代码,程序是否能编译通过?如果可以,输出运行结果;如果不可以,应该怎样 修改?

class Super{
    public final void m1(){
        System.out.println("m1() in Super");
    }
    public void m1(int i){
        System.out.println("m1(int) in Super");
    }
}
class Sub extends Super{
    public void m1(int i){
        System.out.println("m1(int) in Sub");
    }
    public void m1(double d){
        System.out.println("m1(double) in Sub");
    }
}
public class TestMain{
    public static void main(String args[]){
        Sub s = new Sub();
        s.m1();
        s.m1(10);
        s.m1(1.5);
    }


运行结果:
m1() in Super
m1(int) in Sub
m1(double) in Sub


13.仔细阅读以下程序,写出程序运行输出的结果。


class Super{
    public static void m1(){
        System.out.println("m1 in Super");
    }
    public void m2(){
        System.out.println("m2 in Super");
    }
}
class Sub extends Super{
    public static void m1(){
        System.out.println("m1 in Sub");
    }
    public void m2(){
        System.out.println("m2 in Sub");
    }
}
public class Test2{
    public static void main(String args[]){
        Super sup = new Sub();
        sup.m1();
        sup.m2();
        Sub sub =(Sub)sup;
        sub.m1();
        sub.m2(); 
    }
}


运行结果:
m1 in Super
m2 in Sub
m1 in Sub
m2 in Sub


14.下面关于方法声明正确的是( e )


a. abstract final void m()


b. public void final m()


c. static abstract void m()


d. private abstract void m()


e. public static final void m()


15.判断以下描述是否正确,正确填 T;否则填 F。


(1) abstract 可以修饰类、方法、属性(F)


(2) 抽象类中不一定有构造方法(F)


(3) 抽象类只能声明引用,不允许单独 new 对象(T)


(4) 所有类中都可以定义抽象方法(F)


(5) 子类继承抽象类必须实现抽象类中所有抽象方法(T)


(6) static 可以修饰属性、方法、代码块(T)


(7) 静态属性只能用 类名.静态属性名 访问 (F)


(8) 静态方法可以被本类中的其他方法直接访问(T)


(9) 静态代码块在创建对象时完成静态属性的初始化工作(F)


(10)final 只能修饰属性、方法、类(F)


(11)final 修饰的方法不能被继承(F)


(12)final 修饰的类不能被继承,即没有子类(T)


16.代码填空

abstract class Super{
    void m();
    void m2();
}
class Sub extends Super{
    //1.
}
abstract class Sub1 extends Super{
    //2.
}


代码示例:

abstract class Super{
    void m();
    void m2();
}
class Sub extends Super{
    //1.
    void m(){}
    void m2(){}
}
abstract class Sub1 extends Super{
    //2.
}


主观题

(编码)把三大特性中的 Shape 类改为抽象类,并把其中的求周长和求面积的方法改为抽象方法。


代码示例:

abstract class Shape{
    //抽象方法
    public abstract double girth();
    public abstract double area();
}
class Circle extends Shape{
    private double radius;//子类独有的属性 半径
    public Circle(){}
    public Circle(double radius){
        this.radius = radius;
    }
    public double getR() {
        return radius;
    }
    public void setR(double radius) {
        this.radius = radius;
    }
    //子类重写实现了抽象方法
    public double girth(){
        return Math.PI*radius*2;
    }
    public double area(){
        return Math.PI*radius*radius;
    }
}
class Rect extends Shape{
    private double length,width;//长方形的属性 长和宽
    public Rect(){}
    public Rect(double length,double width){
        this.length = length;
        this.width = width;
    }
    public double getLength() {
        return length;
    }
    public void setLength(double length) {
        this.length = length;
    }
    public double getWidth() {
        return width;
    }
    public void setWidth(double width) {
        this.width = width;
    }
    //子类重写了抽象方法 完成了抽象方法的实现
    public double girth(){
        return 2*(length+width);
    }
    public double area(){
        return length*width;
    }
}
class Square extends Rect{
    public Square(){}
    public Square(double length){
        //正方形继承了长方形 长和边相等 所以四个边相等 调用父类的构造方法
        super(length,length);
    }
}

2.(编码)设计一个类 MyClass,为 MyClass 增加一个 count 属性,用来统计总共创建了多少个对象。

public class Test{
    public static void main(String[] args) {
        MyClass mc1 = new MyClass();
        System.out.println(mc1.count);
    }
}
class MyClass{
    static int count;//静态属性 全类成员共享
    public MyClass(){
        //在创建的时候将count自增
        count++;
    }
}

(编码)定义一个类是员工,员工有id和name属性,有一个方法是eat调用eat所有员工输出吃饭,有一 个抽象方法是work,不同的员工工作的内容不同,定义3个类,程序员, 老师,网管,分别调用work 的时候”id为xx的name”写代码”,”讲课”,”拉网线”

  • 代码示例:
//员工类-抽象父类
abstract class Employee{
    private String id;
    private String name;
    public Employee() {}
    public Employee(String id, String name) {
        this.id = id;
        this.name = name;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void eat(){
        System.out.println("吃饭");
    }
    //抽象方法
    public abstract void work();
}
//程序员类
class Programmer extends Employee{
    public Programmer() {}
    public Programmer(String id, String name) {
        super(id, name);
    }
    //实现了员工类中的抽象方法
    public void work(){
        System.out.println("id为"+this.getId()+"的"+this.getName()+"写代码");
    }
}
//老师类
class Teacher extends Employee{
    public Teacher() {}
    public Teacher(String id, String name) {
        super(id, name);
    }
    //实现了员工类的抽象方法
    public void work(){
        System.out.println("id为"+this.getId()+"的"+this.getName()+"讲课");
    }
}
//网管类
class Webmaster extends Employee{
    public Webmaster() {}
    public Webmaster(String id, String name) {
        super(id, name);
    }
    //实现了员工类的抽象方法
    public void work(){
        System.out.println("id为"+this.getId()+"的"+this.getName()+"拉网线");
    }
}

4.(编码)设计一个抽象类,并演示它的三级使用

抽象类 Teacher,有两个方法teach(),add(int a,int b)

抽象子类 MathTeacher,继承于Teacher类,有一个方法teachMath();

第三级类 RealMathTeacher,继承于MathTeacher,有两个方法TeachHightMath(),add(int a,int b);

在主函数中,声明Teacher类引用,并且调用add方法

  • 代码示例:
public class Test {
    public static void main(String[] args){
        Teacher t = new RealMathTeacher();
        System.out.println(t.add(3,5));
    }
}
//抽象父类-里面有抽象方法add
abstract class Teacher{
    public void teach(){
        System.out.println("教学");
    }
    public abstract int add(int a,int b);
}
//抽象子类-没有实现抽象方法add
abstract class MathTeacher extends Teacher{
    public void teachMath(){
        System.out.println("教数学");
    }
}
//普通子类-实现了抽象方法
class RealMathTeacher extends MathTeacher{
    public void teachHighMath(){
        System.out.println("教高数");
    }
    public int add(int a,int b){
        return a+b;
    }
}
相关文章
|
2天前
|
存储 Java 数据库
Static关键字在Java中的多种用途解析
Static关键字在Java中的多种用途解析
|
3天前
|
Java
static关键字在Java中的作用
static关键字在Java中的作用
|
2天前
|
Java
Java面向对象 ( 多态 | final关键字 | 接口 )
Java面向对象 ( 多态 | final关键字 | 接口 )
|
4天前
|
Java
static关键字在Java中的应用场景与示例
static关键字在Java中的应用场景与示例
|
8天前
|
Java
java之final关键字
java之final关键字
10 1
|
1天前
|
存储 Java 数据库
Static关键字在Java中的多种用途解析
Static关键字在Java中的多种用途解析
|
2天前
|
Java
static关键字在Java中的作用
static关键字在Java中的作用
|
5天前
|
Java 数据安全/隐私保护
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
13 0
|
8天前
|
Java 机器人 关系型数据库
Java中的类与接口:抽象与实现的艺术
Java中的类与接口:抽象与实现的艺术
|
10月前
|
前端开发 Java 数据安全/隐私保护
Java教程 (Java 修饰符)
Java教程 (Java 修饰符)