抽象类、接口和内部类
★ abstract 修饰符
-- 修饰类和方法★ 抽象类:使用abstract修饰的类就是抽象类
总结为:有得有失
-- 得:增加了一个功能,可以包含抽象方法
-- 失:不能创建实例
其他的,普通类可以有的,抽象都可以有。
注 意:
1.虽然抽象类不能创建实例,但是有构造器,其构造器主要供其子类的构造器调用;
2.抽象类可以没有抽象方法;
3.抽象类不能创建实例--即使该抽象类没有包含抽象方法;
★抽象方法:用abstract修饰,没有方法体
注 意:
1. 抽象方法不能有方法体;
2.只要方法后出现{ },即使没有任何代码,也算有方法体;
3. abstract 不能与 final 同时出现:
对于abstract方法而言,该方法应该由子类重写,final不允许被 重写,因此不能同时出现;
对于abstract类而言,该类主要用于派生类,final类不允许被继承,因此不能同时出现;
-- abstract 与 final 永远互斥
4. abstract 与 static 不能同时修饰方法;
5 .abstract 与 private 不能同时修饰方法,原因是private意味着该方法不能被子类访问,而abstract要求方法必须由子类重,
二者冲突,因此不能同时修饰方法;
★抽象类的作用:抽象类主要是与“模板模式”联系在一起
-- 在为抽象类定义子类时,子类必须实现抽象父类的所有抽象发方法,否则子类也必须定义为抽象类。
★接口:接口是从多个相似类中提取出来的的一种规范,接口所体现出来的 也是种规范(可以把接口理解为“完全抽象、彻底抽象”的特殊类)
接口定义:
[修饰符] interface 接口名
{
//Field定义
//抽象方法定义
//内部类、内部接口、内部枚举定义
}
修饰符:只能是 public 或 省略
注 意:
1. 接口不能有构造器,也不能有初始化块;
2. 接口里的field,默认(不管写不写都有)有3个修饰符:public static final
3.接口里的方法,默认(不管写不写都有)有2个修饰符:public abstract (因为默认有 abstract,即必须是抽象方法 ,所以不能有static)
4.接口里的内部类、内部接口、内部枚举,默认(不管写不写都有 )有2个修饰符: public static
5.一个接口可以有n个父接口
接口的用途:
1.接口可以用来定义变量;
2.接口不可直接创建实例;
3.接口最大的用途就是供其它类来实现接口本身;
-- 实现接口用关键字:implements
-- 当一个类实现接口之后,类必须为接口的所有抽象方法提供实现,否则实现类也只能是抽象类。
接口主要是在“面向接口编程”时提供更灵活的机制;
接口与抽象类之间的相似之处:
1.都可以包含抽象方法;
2.都不能创建实例;
3. 子类继承抽象类或是实现接口,都要求实现所有的抽象方法,否则子类也只能是抽象类;
接口与抽象类之间的区别:
1.接口里只能有抽象方法,但抽象类可以没有抽象方法,只包含普通方法;
2.接口里不能定义静态方法,但抽象类可以包含静态方法;
3.接口里的Field总是有 public static final修饰,但抽象类中的Field 可以是普通的Field;
4.接口不能包含构造器,但抽象类可以有构造器;
5.接口不能包含初始化块,但抽象类完全可以有初始化块;
6.接口可以有多个直接的父接口,但抽象类只能有一个直接父类。
★内部类:
-- 由于它的上一级单元是类,因此可以用 private | protected |默认 | public 这4个访问控制符
-- 由于内部类的上一级程序单元是类,可以用static修饰,有
static 修饰的内部类属于外部类本身,没有static修饰的内部类属于外部类的实例。
内部类的生成的class文件名为: 外部内$内部类.class
****************** 非静态内部类 *******************
1.非静态内部类可以直接访问外部类的 private 成员(field、方法、构造器);
2.非静态内部类可以直接访问外部类的成员,包括 private 成员,但是外部类不可以访问内部类的成员;
-- 内部类也叫“寄生类”
没有 static 修饰的内部类的实例,必须“寄生”在 外部类 的对象里;
有 static 修饰的的内部类的实例,“寄生”在 外部类 的类本身里。
-- 非静态内部类,也是属于非静态成员(因为不能被外部类的静态成员所访问);
***************************************************
****************** 静态内部类 *********************
静态内部类 -- 要用 static 修饰
-- static 修饰的内部类的实例,“寄生”在 外部类 的类本身里,静态内部类也属于静态成员,因此不能访问外部类的非静态成员(Field、方法)
内部类的使用
-- 定义变量、创建实例、访问类Field/类方法
1.在外部类的里面使用内部类,和使用一般的类Field、类方法相似;
2.在外部类外面使用内部类
-- 非静态内部类
① 声明变量:外部类.内部类 变量名
② 创建非静态内部类的实例:
必须先创建外部类的实例(必须先创建宿主),再用以下方式
宿主.new 外部类构造器();
③ 非静态内部类派生子类:
由于子类的构造器必须调用父类的构造器一次,因此必须在子类构造器中使用宿主对象去调用非静态内部类的构造器。
-- 静态内部类
静态内部类是寄生在外部类的类本身里的,所以一般不需要程序员去理会宿主,可以直接把外部类当成类的包。
★匿名内部类
-- 匿名内部类是一个“没有名字”的内部类,所以程序以后无法访问这个“匿名内部类”;当程序创建匿名内部类时,会立即创建匿名内部类的实例;
-- 匿名内部类的语法:
new 接口()|父类构造器(参数)
{
//类体部分
}
注意:该语法格式实际上返回匿名内部类的实例,程序以后只能使用内部类的实例。
匿名内部类的规则:
① 匿名内部类必须显示继承一个父类或是实现 1个 接口;
② 匿名内部类必须实现接口或抽象类中所有抽象方法 -- 因为匿名内部类需要立即创建实例,因此它不能是抽象的。
③ 匿名内部类不能有构造器 -- 因为匿名内部类没有名字,构造器名无法确定。
★ 枚举
-- 用于代表 实例已经固定 的类,而且定义时必须在 第一行 创建并列出来。
定义语法:
修饰符 enmu 枚举名
{
//第一行列出该枚举的所有实例
}
-- 修饰符 可以是 public |省略、 但 abstract | final 必有其一
-- 所有的枚举类都有一个 values()方法,该方法返回所有的枚举实例
● 定义枚举后,其实例已经创建,直接使用即可。
● 枚举类可以有 field、方法、 构造器(只能是private修饰,无论是否显示写出,默认总是有)、初始化块、内部内;
● 列出枚举值,并不是简单的定义几个枚举值的变量名,其实质是调用枚举类的构造器来创建相应的实例
例如:
代码1:
public enum Season
{
//下面四个实例,相当于是 public static final 修饰的Field
SPRING,SUMMER,FALL,WINTER;
/*******************************************************
上面直接写出的四个实例的实质是调用系统提供的无参构造器
来创建枚举类实例,因此可以写成以下方式:
SPRING(),SUMMER(),FALL(),WINTER();
public static void main(String[] args)
{
// 用foreach循环和枚举类的values()方法输出
for(Season S : Season.values())
{
System.out.println(t);
}
}
}
代码2:
public enum Gender
{
MALE("man"),FEMALE("women");
/**************************************
以上代码是调用自定义的有参构造器,其本质写法是
public static final Gender MALE = new Gender("man");
public static final Gender FEMALE =new Gender("woman");
**************************************/
private String name;
//自定义有参构造器,构造器默认有private修饰,所以也可以省略 //不写
private Gender(String name)
{
this.name = name;
}
}
● 包含抽象方法的枚举类(抽象枚举)
例如:
public enum Operation
{
/*如果枚举类已经定义了抽象方法,该枚举类默认就有了abstract修饰,
此时该枚举类不能有 final 修饰,此时该枚举类为抽象枚举类,而抽
象类不能创建实例,但枚举类要创建实例,因此用 匿名内部类来解决
这个矛盾,例如要创建 PLUS(加)、SUB(减) 实例,实现如下:*/
{
public double eval(double x,double y)
{
return x + y;
}
},
SUB()
{
public double eval(double x,double y)
{
return x - y;
}
};
//定义抽象方法
public abstract double eval(double x,double y);
public static void main(String[] args)
{
// 输出格式:枚举类名.枚举实例.方法
System.out.println(Operation.PLUS.eval(10.0,9.1));
System.out.println(Operation.SUB.eval(10.0,9.1));
}
}
● 枚举可以实现接口
① 实现接口,并直接实现所有的抽象方法,此时枚举类就不再使用抽象枚举;
② 实现接口,但并不直接实现抽象方法,此时就变成了 包含抽象发方法的枚举类(抽象枚举)。