JavaSE 第七章 面向对象基础(下)静态&枚举&抽象类


活动地址:CSDN21天学习挑战赛

7.1 静态

7.1.1 静态关键字(static)

在类中声明的实例变量,它的值是每个对象独立的。但是有些成员变量的值不能每个对象独一份,需要成为共享数据

在类中声明的实例方法,想要调用它时就必须先创建对象,使用对象来调用,但是有些方法是和当前类对象无关的。

对于以上的两种情况,我们就需要将类的成员变量和成员方法声明成静态的(static)
7.1.2 静态变量

1、语法格式

static修饰的成员变量就是静态变量

[修饰符] class{
	[修饰符] static 数据类型 静态变量名;
}
2、静态变量的特点
  • 静态变量的默认值就是其所对应的数据类型的默认值
  • 静态变量是所有对象共享的
  • 静态变量的值存储在方法区
  • 静态变量在本类中,可以在任何方法、代码块、构造器中直接使用
  • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。
  • 静态变量的get/set方法也是静态的,当局部变量与静态变量重名时,使用”类名.静态变量“进行区分。
  • 静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。
  • 同一个类中,静态成员只能访问静态成员,这是生命周期原因,反过来是可以用的 main方法为一个程序入口,可以应用于任何类。
  • 静态代码块,只执行一次

示例:

public class Student {
    private String name ;
    private int age ;
    static int num ;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", num=" + num +
                '}';
    }
}


class Test1 {
    public static void main(String[] args) {
        // 通过类名.静态变量名访问静态变量
        System.out.println("num的默认值:" + Student.num);  

        Student s1 = new Student("张三" , 23);
        // 在修饰符允许的情况下,可以通过对象.静态变量名进行访问静态变量,但是不建议
        s1.num = 3 ;
        System.out.println(s1);
        Student s2 = new Student("李四" , 25);
        System.out.println(s2);

    }
}
3、 静态变量、非静态实例变量和局部变量
  • 静态类变量(静态变量):和类有关,存储在方法区,有默认值,是所有对象所共享的,生命周期和类相同,可以使用权限修饰符修饰

  • 非静态实例变量(实例变量):跟对象有关,存储在堆中,有默认值,每一个对象的实例变量是独立的,生命周期和对象相关,可以使用权限修饰符修饰

  • 局部变量:和方法有关,存储在栈中,没有默认值,每一次调用方法都是独立的,作用域就是方法体内,只能用final修饰符修饰

7.1.3 静态方法

1、语法格式

static修饰的成员方法就是静态方法

[修饰符] class{
	[修饰符] static 返回值类型 方法名(形参列表) {
    	方法体
    }
}
2、静态方法的特点
  • 静态方法在本类的任意方法、代码块、构造器中都可以被直接调用

  • 只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。

  • 静态方法可以被子类继承,但不能被子类重写。

在这里插入图片描述

  • 静态方法的调用都只看编译时类型。
public class Animal {
    private String name ;
    private int age ;

    {
        System.out.println("这是一个非静态代码块");
        method();
    }

    public Animal() {
        System.out.println("无参构造");
        method();
    }

    public Animal(String name, int age) {
        System.out.println("有参构造");
        this.name = name;
        this.age = age;
        method();
    }

    public static void method() {
        System.out.println("静态方法method()被调用");
    }

    public void getInfo() {
        System.out.println("name:" + name + ",age:" + age);
        method();
    }

}

class Cat extends Animal{

    public Cat() {
    }

    public Cat(String name, int age) {
        super(name, age);
    }

//    public void method() {
//        System.out.println("静态方法method()被调用");
//    }

}

class Test4 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Animal.method();
        Cat.method();
    }
}

在这里插入图片描述

7.1.4 静态代码块

如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。

1、语法格式
  • 代码块的前面加static,就是静态代码块
[修饰符] class 类名 {
	static {
    	静态代码块
    }
}
2、静态代码块的特点

每一个类中的静态代码块只会执行一次,且静态代码块的执行优先于构造器和非静态代码块

示例:

public class StaticCode {
    static {
        System.out.println("StaticCode类中的静态代码块");
    }

    {
        System.out.println("StaticCode类中的非静态代码块");
    }

    public StaticCode() {
        System.out.println("无参构造器");
    }
}

class Test1 {
    public static void main(String[] args) {
        StaticCode s1 = new StaticCode();
        System.out.println("------------------");
        StaticCode s2 = new StaticCode();
    }
}

在这里插入图片描述

3、静态代码块和非静态代码块
  • 静态代码块和非静态代码块都是写在类中方法外的

  • 静态代码块先于非静态代码块执行

  • 静态代码块在类初始化时执行,且只执行一次

  • 非静态代码块在实例化对象时执行,每创建一个对象都会执行一次非静态代码块

7.1.5 类初始化

  • 类的初始化就是为静态语句初始化的。实际上,类初始化的过程时在调用一个clinit()方法,而这个方法是编译器自动生成的。编译器会将静态类成员变量的显示赋值语句和静态代码块中的语句按顺序合并到类初始化clinit()方法中

  • 每一个类都只会进行一次初始化,如果子类初始化时发现父类还未初始化,那么会先将父类初始化

  • 类的初始化一定是优先于对象的实例化的

1、类的初始化代码只执行一次
public class Father {

    static {
        System.out.println("Father的静态代码块1 , a = " + Father.a);
    }

    private static int a = 8 ;

    static {
        System.out.println("Father的静态代码块1 , a = " + Father.a);
    }

    public static void method() {
        System.out.println("Father.method");
    }
}

class Test2 {
    public static void main(String[] args) {
        Father.method();
        Father f1 = new Father();
        Father f2 = new Father();
    }
}
2、父类优先于子类初始化
public class Animal {
    static {
        System.out.println("Animal类中的静态代码块");
    }
}

class Cat extends Animal {
    static {
        System.out.println("Cat类中的静态代码块");
    }
}

class Test3 {
    public static void main(String[] args) {
        Cat cat = new Cat();
    }
}

在这里插入图片描述

3、类初始化优先于实例初始化
public class Person {
    
    static {
        System.out.println("Person类的静态代码块");
    }
    
    {
        System.out.println("Person类的非静态代码块");
    }
    
    public Person() {
        System.out.println("Person类的无参构造");
    }
}

class Test4 {
    public static void main(String[] args) {
        Person person = new Person();
    }
}

在这里插入图片描述

7.1.6 静态和非静态的区别

1、本类中的访问限制区别
  • 静态的类变量和静态的方法可以在本类的任意方法、代码块、构造器中直接访问。
  • 非静态的实例变量和非静态的方法只能在本类的非静态方法、非静态代码块、构造器中直接访问

即:

  • 静态直接访问静态,可以

  • 非静态直接访问非静态,可以

  • 非静态直接访问静态,可以

  • 静态访问非静态,不可以

2、在其他类的访问方式区别
  • 静态的类变量和静态的方法可以通过“类名.”的方式直接访问;也可以通过“对象."的方式访问。(但是更推荐使用”类名."的方式)

  • 非静态的实例变量和非静态的方法只能通过“对象."方式访问。

3、this和super的使用
  • 静态的方法和静态的代码块中,不允许出现this和super关键字,如果有重名问题,使用“类名.”进行区别。(this和super关键字都是和对象有关的)

  • 非静态的方法和非静态的代码块中,可以使用this和super关键字。

7.2 枚举

7.2.1 概述

某些类型的对象是有限的几个,例如:

  • 星期:Monday(星期一)…Sunday(星期天)

  • 性别:Man(男)、Woman(女)

  • 月份:January(1月)…December(12月)

  • 季节:Spring(春节)…Winter(冬天)

  • ……
  • 枚举类型本质上也是一种类,只不过是这个类的对象是固定的几个,而不能随意让用户创建。

  • 在JDK1.5之前,需要程序员自己通过特殊的方式来定义枚举类型。

  • 在JDK1.5之后,Java支持enum关键字来快速的定义枚举类型。

7.2.2 JDK1.5之前

在JDK1.5之前声明枚举类

  • 构造器加private私有化

  • 本类内部创建一组常量对象,并添加public static修饰符,对外暴露这些常量对象

示例代码:

/**
 * JDK1.5之前定义枚举类的方法
 */
public class Season {
    public static final Season SPRING = new Season() ;
    public static final Season SUMMER = new Season() ;
    public static final Season AUTUMN = new Season() ;
    public static final Season WINTER = new Season() ;

    private Season() {}

    @Override
    public String toString() {
        if (this == SPRING) {
            return "春" ;
        }else if (this == SUMMER) {
            return "夏" ;
        } else if (this == AUTUMN) {
            return "秋" ;
        } else {
            return "冬" ;
        }
    }
}


class TestSeason {
    public static void main(String[] args) {
        Season spring = Season.SPRING ;
        System.out.println(spring);	// 春
        System.out.println(Season.SPRING);	// 春
    }
}

7.2.3 JDK1.5之后

1、enum关键字声明枚举

格式:

[修饰符] enum 枚举类名 {
	常量对象列表
}


[修饰符] enum 枚举类名 {
	常量对象列表;
  	
  	其他成员列表;
}

代码示例:

/**
 * 枚举类,JDK1.5以后新增的枚举类声明方式
 */
public enum  Week {
    MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY , SUNDAY
}

class TestWeek {
    public static void main(String[] args) {
        Week week = Week.FRIDAY ;
        System.out.println(week);	// FRIDAY 
    }
}
2、枚举类的要求和特点

枚举类的要求和特点:

  • 枚举类的常量对象列表必须在枚举类的首行,因为是常量,所以建议大写。

  • 如果常量对象列表后面没有其他代码,那么“”可以省略,否则不可以省略“;”。

  • 编译器给枚举类默认提供的是private的无参构造,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数,

  • 如果枚举类需要的是有参构造,需要手动定义,有参构造的private可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。

  • 枚举类默认继承的是java.lang.Enum类,因此不能再继承其他的类型。

  • JDK1.5之后switch,提供支持枚举类型,case后面可以写枚举常量名。

  • 枚举类型如有其它属性,建议(不是必须)这些属性也声明为final的,因为常量对象在逻辑意义上应该不可变。

示例代码:

public enum  Week1 {
    MONDAY("星期一") ,
    TUESDAY("星期二") ,
    WEDNESDAY("星期三") ,
    THURSDAY("星期四") ,
    FRIDAY("星期五") ,
    SATURDAY("星期六") ,
    SUNDAY("星期日") ;

    private final String description ;

    private Week1(String description) {
        this.description = description ;
    }

    @Override
    public String toString() {
        return super.toString()+ ":" + description;
    }
}

class TestWeek1 {
    public static void main(String[] args) {
        System.out.println(Week1.SATURDAY);

        Week1 week1 = Week1.TUESDAY;

        switch (week1) {
            case MONDAY:
                System.out.println("今天是周一");
                break;
            case TUESDAY:
                System.out.println("今天是周二");
                break;
            case WEDNESDAY:
                System.out.println("今天是周三");
                break;
            case THURSDAY:
                System.out.println("今天是周四");
                break;
            case FRIDAY:
                System.out.println("今天是周五");
                break;
            case SATURDAY:
                System.out.println("今天是周六");
                break;
            case SUNDAY:
                System.out.println("今天是周日");
                break;
        }
    }
}

枚举类型常用方法

  • String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法

  • String name():返回的是常量名(对象名)

  • int ordinal():返回常量的次序号,默认从0开始

  • 枚举类型[] values():返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法

  • 枚举类型 valueOf(String name):根据枚举常量对象名称获取枚举对象

示例:

public class Demo2 {
    public static void main(String[] args) {

        System.out.println(Season1.SPRING.name()); // 返回的是常量名(对象名)

        System.out.println(Season1.WINTER.ordinal());    // 返回常量的次序号,默认从0开始

        Season1[] season1s = Season1.values();  // 返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
        for (Season1 season1 : season1s) {
            System.out.println(season1);
        }

        Season1 spring = Season1.valueOf("SPRING");
        System.out.println(spring);

    }
}

enum Season1 {
    /**
     * 季节枚举类
     */
    SPRING("春天") , SUMMER("夏天") , AUTUMN("秋天") ,WINTER("冬天") ;

    private final String description ;

    Season1(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return super.toString() + ":" + description;
    }
}

7.3 抽象类

7.3.1 概述

  • 抽象:不具体、或无法具体

例如:当我们声明一个几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现,而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类必须是抽象类。

  • 概述: 使用abstract关键字修饰的类就是抽象类
  • 特点: 这种类不能被创建对象,它一般就是用来做父类的,被子类继承的

7.3.2 语法格式

  • 抽象方法:

    • 被abstract修饰

    • 没有方法体的方法

  • 抽象方法的作用:

    • 强制要求子类必须重写
  • 抽象类

    • 被abstract修饰的类。

抽象类的语法格式:

[权限修饰符] abstract class 类名 {

}

[权限修饰符] abstract class 类名 extends 类名 {

}

抽象方法的语法格式

[修饰符] abstract 返回值类型 方法名([形参列表]) ;
  • 抽象方法没有方法体

示例:

/**
 * 定义一个抽象类
 */
public abstract class Animal {
    public abstract void eat() ;    // 动物都会有的行为,定义为抽象方法,必须要实现
    public abstract void sleep() ;

    // 一些动物会有的行为定义为普通方法,不要求必须重写
    public void fly() {

    }
}

class Cat extends Animal{

    @Override
    public void eat() { // 小猫类实现吃东西的方法
        System.out.println("小猫吃鱼");
    }

    @Override
    public void sleep() {   // 小猫类实现睡觉的方法
        System.out.println("小猫睡觉");
    }
}

class TestAbstract {
    public static void main(String[] args) {
        Animal cat = new Cat() ;

        cat.eat();	// 小猫吃鱼
        cat.sleep();	// 小猫睡觉
    }
}

此时的放法重写是子类对父类抽象方法的完成实现,我们将这种叫做方法的重写,也叫方法的实现

7.3.3 注意事项

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
    • 假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

在这里插入图片描述

  • 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。
    • 子类的构造方法中,有默认的super()或手动的super(实参列表),需要访问父类构造方法。

在这里插入图片描述

  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
    • 未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

在这里插入图片描述

  • 有抽象方法的类一定是抽象类
    在这里插入图片描述

  • 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

    • 假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

在这里插入图片描述
在这里插入图片描述

  • 抽象类的成员特点就是普通类+抽象方法

7.3.4 抽象类的应用场景

  • 在父类中,需要定义该共性功能,但具体实现要求子类自己去完成,有这种需求时,该功能定义为抽象的,abstract
  • 同理,如果父类中的共性功能,非必要子类必须去实现,即可定义为普通方法
  • 抽象类的定义和设计,更多的还是当做一个继承体系的父类,使得子类继承该父类的共性属性和功能
  • 在遇到需要子类必须去实现功能时,定义抽象功能
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值