七、抽象类、接口、枚举
一、抽象类
Java 允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
abstract 可以用来修饰的结构:类、方法
abstract 修饰类:抽象类
此类不能实例化,抽象类本就是包含有无法实例化的抽象方法,或者说这个方法是没有任何意义的,他存在的意义就是让子类去实现它;因此抽象类是不可以实例化的,也就是不能创建对象;
继承抽象类的子类必须重写父类所有的抽象方法。原因是因为有继承关系,继承了抽象方法,因此要么子类变为抽象类,要么实现方法体
类该有的成员抽象类都可以有。
必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化全过程)
abstract 修饰方法:抽象方法
抽象方法,只有方法的声明,没有方法体。
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法), 缺省情况下默认为public。
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
若子类重写了父类中所有的抽象方法,此子类方可实例化。
若子类没有重写了父类中所有的抽象方法,则此子类也是一个抽象类。
abstract 使用上的注意点:
abstract 不能用来修饰变量、代码块、构造器;
abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类。
二、关键字
instanceof
a instanceof A:判断对象a是否是类A的实例。如果,返回true,如果不是,返回false;
使用情境:为了避免在向下转型时出现ClassCastException异常,我们在进行向下转型之前,先进行
instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
如果a instanceof A返回true,则a instanceof B也返回true。 其中类B是类A的父类。
this
- this 是用来修饰,和调用 本类中的字段,实例方法,构造器
- this是一个引用,this是一个变量,this变量中保存了内存地址指向了自身,this存储在JVM堆内存中实例对象的内部。 因此在调用时哪个对象使用了this,this就代表哪个对象
- this的三个含义,表示类的实例变量,类的实例方法,类的实例对象
- 注意:this只能指代非静态的字段和方法
- 以后调用本类成员都叫this,逻辑会更加清晰
super
- super关键字:用于修饰父类成员变量,类似this ;this代表的是本类对象,而super代表的是父类对象;使用super我们可以调用父类的成员(属性和行为),注意super关键字不能访问父类私有(private修饰)的成员
- 子父类中出现了同名的实例变量和静态时,在子类中需要访问父类中非私有成员变量(不是private修饰的)时,需要使用 super 关键字,修饰父类成员变量,类似于之前学过的 this 。
- 必须声明在子类构造器的首行,对于"this(形参列表)"或"super(形参列表)"只能二选一,至少有一个类的构造器使用了"super(形参列表)",调用父类中的构造器。
- super的关键字的作用如下:
- 用于访问父类中定义的属性
- 用于调用父类中定义的成员方法
- 用于在子类构造方法(隐藏)中调用父类的构造器
final
- 修饰类,类不能被继承, 被final修饰的类所有成员方法都将被隐式修饰为final方法.
- 修饰方法,方法不能子类重写, 在编译器对方法进行内联, 提升效率.
- 修饰变量,变量只能被赋值一次,之后不能再修改,因此也被称为常量,名称字母全大写
- 当final修饰的是一个基本数据类型数据时, 这个数据的值在初始化后将不能被改变; 当final修饰的是一个引用类型数据时, 也就是修饰一个对象时, 引用在初始化后将永远指向一个内存地址, 不可修改. 但是该内存地址中保存的对象信息, 是可以进行修改的.
- final是用于定义常量的, 定义常量的好处是: 不需要重复地创建相同的变量. 而常量池是Java的一项重要技术, 由final修饰的变量会在编译阶段放入到调用类的常量池中.
static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。
我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。
static 可以用来修饰:属性、方法、代码块、内部类。被修饰的成员是属于类的,而不是单单是属于某个对象的。被static修饰的成员由该类的所有实例(对象)共享;
类变量
当 static 修饰字段时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作,因为该变量属于类,而不是某个对象。 静态变量的加载要早于对象的创建。由于类只会加载一次,则静态成员变量在内存中也只会存在一次。存在方法区的静态域中。
静态属性 VS 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有了一套类中的非静态属性当修改其中一个非静态属性时,不会导致其他对象中同样的属性值的修饰。
静态成员变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过静态变量去修改某一个变量时,会导致其他对象调用此静态变量时,是修改过的。
类方法
当 static 修饰方法时,该方法称为类方法,可以通过类名.方法名直接调用
注意事项 :通过生命周期进行分析
1.静态成员方法中,不能使用非静态变量(实例变量)和非静态方法(实例方法),因为静态方法,可以通过类名调用可以不用创建实例对象,而实例变量和方法都是随着实例对象的产生而产生,随着对象的死亡而死亡
2.静态方法中不能使用this 和 super 关键字 ,因为不需要创建对象,this指代本类的实例对象,super指代的是父类的实例对象
3.非静态方法中随意使用静态方法,静态成员变量,因为对象产生时,静态成员变量,方法早已经存在,可以直接使用
4.java中静态成员变量和静态成员方法可以被继承,但是没有被重写(overwrite)而是被隐藏
总结:静态成员方法只能使用静态变量和 调用静态成员变量,非静态方法可以调用所有本类方法和属性
静态代码块
静态代码块:定义在成员位置,使用static修饰的代码块{ }。
- 位置:类中方法外。
- 执行:随着类的加载执行,而执行且执行一次。
作用:随着类加载给类变量进行初始化赋值。
abstract、static、final都不能用于修饰构造方法
- abstract 被abstract 修饰的方法不能有方法体,而构造方法是用来给实例进行初始化的,没有方法体不能进行初始化
- static 因为jvm进行类加载时会吧所有的静态成员先于对象加载进堆,一是,构造方法是用来给对象进行初始化的,此时还没有对象,二是因为可以通过类名.方法名调用方法,但是构造方法不允许手动调用
- final方法是约束子类不能覆盖,但是构造方法本身不允许子类继承。谈不上覆盖。(构造方法不是通过覆盖得到的,所以没必要用final修饰)
三、接口
接口是抽象方法和全局常量的集合 ( 在JDK7及以前,接口中只含有抽象方法;在JDK8中,接口可以含有默认方法以及静态方法;JDK9中,接口可以含有私有方法),是一种引用类型,在java中,类和接口是并列的结构,可以理解为一种更加特殊的抽象类( 接口是对行为的抽象 ),用interface声明
接口的特点
- 接口中所有的字段都默认是由public static final 修饰的(可不显式声明)
- public : 接口和接口,类之间会形成多层继承关系,子类也会继承到字段,定义为pubilc保证访问范围
- static : 如果不定义为static,接口只有实例化才能访问到,但是接口连构造器都没有,不能进行实例化
- final : 如果不定义为final , 接口中的方法是abstract ,没有方法体,不能在方法修改字段值,虽然可以直接修改静态成员变量,但是所有的实现类和子接口的继承的值也都会被改变,此做法等同于抽象类,风险也极高,所以定义为final
- 接口中所有的方法都默认是由public abstract 修饰的,限制方法不能有方法体,实现的类必须重写
- 接口中没有构造器(不能实例化对象)
- 类实现接口必须重写接口中所有的抽象方法,否则该类为抽象类
- 接口之间采取多继承制
- 接口中不能有静态代码块,接口中的字段都在定义时初始化了,不需要静态代码块和构造器进行初始化
- 接口中的静态方法不能被继承
- 接口中可以有内部类https://blog.csdn.net/csucsgoat/article/details/108764455
接口的方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写,默认方法可以重写也可以不重写,不重写默认被继承下来;
静态方法:使用 static 修饰,供接口直接调用(接口中,被static修饰的方法不能被继承到子类)
私有方法 : 使用private修饰,可以用于提取多个默认方法共同内容,是对默认方法和静态方法的辅助
接口的多实现
- 继承或实现多个接口,接口中有重命的方法必须显式重写或实现,由子类决定其功能
- 静态方法重名不会冲突,因为不会继承
- 同时继承父类和实现多个父接口,有重名的方法时,实现的方法默认时父类的方法
四、接口与抽象类的区别
- 抽象类可以有成员方法的实现细节,而接口中没有实现细节
- 抽象类有构造器,接口没有
- 抽象类中可以有成员变量,而接口中只能有全局变量(pubilc static final)
- 抽象类中可以有静态代码块,接口中没有
- 抽象类是单继承,接口是多继承
- 抽象类多用于模板设计模式,接口多用于 简单工厂、工厂方法、代理模式
五、枚举
枚举(enum),全称enumeration,是JDK 1.5 中引入的新特性。Java 枚举是一个特殊的类,一般表示一组常量
在JDK1.5 之前,我们定义常量都是: public static fianl。有了枚举之后,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。
Java 枚举类使用enum关键字来定义,各个常量使用逗号来分割。如果枚举中有其他成员结构,结尾必须用;结束
枚举中每一个枚举项都是一个实例对象,类型是枚举类型 ,并且只能通过枚举名.枚举项调用,不可以new 枚举类
枚举成员
枚举类和正常的类一样,也可以有类的五大结构
public enum Season {
SPRING("春"),SUMMER,AUTUMN,WINTER;//当枚举项后面有其他成员,最后一个枚举项必须加分号
private String name;//字段
public static String month;//类变量
static {
month = "1-3";
}//静态代码块
private Season() {}//无参构造器,枚举类的所有构造器默认是私有的,且只能是私有的
private Season(String name) {
this.name = name;
}//有参构造器
public void show(){}//成员方法
public static void send(){}//静态成员方法
class Date{}//内部类
}
枚举的构造器
枚举的构造器默认是私有的,且只能是私有的,因为枚举项是固定的,不能让外界创建枚举的实例
枚举的枚举项默认都是通过枚举类的无参构造器创建的, 每使用一次枚举项都会初始化里面的所有无参枚举项;使用枚举项就等同于调用本类的无参构造器
枚举项本质是枚举的实例对象,因此可以在定义的时候调用有参构造器传递参数
当枚举类中含有抽象方法的时候,定义枚举项时,实现接口,必须重写该枚举类或接口中所有的抽象方法
public enum Season {
SPRING(){
@Override
public void fun() {
System.out.println("我是春天~");
}
};
public abstract void fun();
}
枚举类除了不能拥有Object中的clone、finalize方法外,其他方法都能拥有;
import 和 import static的区别
- import static静态导入是JDK1.5中的新特性
- 一般我们导入一个类都用 import 包名.类名
- 而静态导入是这样:import static 包名.类名.*
这里的多了个static,还有就是类名后面多了个 .* 。意思是导入这个类里的静态成员(静态方法、静态变量)。当然,也可以只导入某个静态方法,只要把 .* 换成静态方法名就行了。然后在这个类中,就可以直接用方法名调用静态方法,而不必用“类名.方法名()” 的方式来调用。
这种方法的好处就是可以简化一些操作,例如一些工具类的静态方法,如果使了静态导入,就可以像使用自己的方法一样使用这些静态方法。
注意点: 不过在使用静态导入之前,我们必须了解下面几点:
- 静态导入可能会让代码更加难以阅读
- import static和static import不能替换
- 如果同时导入的两个类中又有重命名的静态成员,会出现编译器错误。例如Integer类和Long类的MAX_VALUE。
- 可以导入的静态成员包括静态对象引用、静态常量和静态方法。

浙公网安备 33010602011771号