抽象类与接口
1、抽象方法
如果一个方法只有方法的声明而没有具体的方法实现,这个方法就叫做抽象方法,Java 中的抽象方法需要使用 abstract 关键字来修饰。
public abstract void buyBook();
抽象方法与普通方法的区别是抽象方法没有方法体。
2、抽象类
一旦类中定义了抽象方法,则该类也必须声明为抽象类,需要在类定义处添加 abstract 关键字。
public abstract class Member {
public abstract void buyBook();
}
抽象类与普通类的区别是抽象类不能被实例化。
抽象类中可以没有抽象方法,但是包含了抽象方法的类必须定义为抽象类。即我们可以在抽象类中定义普通方法,但是在普通类中不能定义抽象方法。
如果父类是抽象类,一旦子类继承了该抽象父类,则子类要么对父类的抽象方法进行重写,要么子类也成为抽象类,则可以不用重写父类的抽象方法。
public abstract class Member {
public abstract void buyBook();
}
public class SuperMember extends Member {
@Override
public void buyBook() {
// TODO Auto-generated method stub
System.out.println("超级会员买书打6折");
}
}
3、接口
- 什么是接口?
接口是由抽象类衍生出来的一个概念,并由此产生了一种编程方式:面向接口编程。
面向接口编程就是将程序中的业务模块进行分离,以接口的形式去对接不同的业务模块。
面向接口编程的优点:当用户需求变更时,只需要切换不同的实现类,而不需要修改串联模块的接口,减少对系统的影响。
1、能够最大限度实现解耦合,降低程序的耦合性。
2、使程序易于扩展。
3、有利于程序的后期维护。
- 如何使用接口
接口在 Java 中时独立存在的一种结构,和类相似,我们需要创建一个接口文件,Java 中用 class 关键字来标识类,用 interface 来标识接口,基本语法:
public interface 接口名{
public 返回值 方法名(参数列表)
}
接口其实就是一个抽象类,极度抽象的抽象类。
抽象类:一个类中一旦存在没有具体实现的抽象方法时,那么该类就必须定义为抽象类,同时抽象类允许存在非抽象方法。
但是接口完全不同,接口中不能存在非抽象方法,接口中必须全部是抽象方法。
因为接口中必须全部都是抽象方法,所以修饰抽象方法的关键字 abstract 可以省略。
接口中允许定义成员变量,但是有如下要求:
1、不能定义 private 和 protected 修饰的成员变量,只能定义 public 和默认访问权限修饰符修饰的成员变量。
2、接口中的成员变量在定义时就必须完成初始化。
3、接口中的成员变量都是静态常量,即可以直接通过接口访问,同时值不能被修改。
4、接口中的方法默认都是abstract public
5、接口中的变量都是static final ,相当于是类常量
public interface MyInterface {
public int ID = 0;
String NAME = "张三";
public void test();
}
使用接口时,不能直接实例化接口对象,而必须实例化其实现类对象,实现类本身就是一个普通的 Java 类,创建实现类的代码如下所示。
public class MyInterfaceImpl implements MyInterface {
@Override
public void test() {
// TODO Auto-generated method stub
}
}
通过 implements 关键字来指定实现类具体要实现的接口,在实现类的内部需要对接口的所有抽象方法进行实现,同时要求访问权限修饰符、返回值类型、方法名和参数列表必须完全一致。
接口和继承,Java 只支持单继承,但是接口可以多实现(一个实现类可以同时实现多个接口)
public interface MyInterface {
public int ID = 0;
String NAME = "张三";
public void fly();
}
public interface MyInterface2 {
public void run();
}
public class MyInterfaceImpl implements MyInterface,MyInterface2 {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("实现了跑步的方法");
}
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("实现了飞行的方法");
}
}
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
MyInterfaceImpl myInterfaceImpl = new MyInterfaceImpl();
myInterfaceImpl.fly();
myInterfaceImpl.run();
}
}
4、内部类
将一个类定义在另一个给类里面或者方法里面,这样的类就被称为内部类。
内部类可以分为四种:成员内部类、局部内部类、匿名内部类、静态内部类。
-
成员内部类
将一个类定义在另一个类中。一般定义格式如下:
class Outer{ class Inner{ } }
因为类Outer相对于类Inner在外面,我们且称类Outer为外部类。
成员内部类可以无条件访问外部类的属性和方法,但是外部类想要访问内部类属性或方法时,必须要创建一个内部类对象,然后通过该对象访问内部类的属性或方法。
**成员内部类无条件访问外部类的属性和方法:**
```java
class Outer{
private String name = "外部类";
public void run(){
System.out.println("外部类奔跑");
}
class Inner{
public void say(){
System.out.println(name);
run();
}
}
}
外部类访问内部类属性和方法
class Outer{
private String name = "外部类";
public void run(){
System.out.println("外部类奔跑");
}
/*使用内部类的属性和方法*/
public void eat(){
Inner inner = new Inner();
System.out.println(inner.value);
inner.say();
}
class Inner{
private String value = "DDD";
public void say(){
System.out.println(name);
run();
}
}
}
外部类属性或方法隐藏
如果成员内部类的属性或者方法与外部类同名,将导致外部类的这些属性与方法在内部类被隐藏,也可按照该格式调用,外部类.this.属性/方法。
class Outer{
private String name = "外部类";
public void run(){
System.out.println("外部类奔跑");
}
/*使用内部类的属性和方法*/
public void eat(){
Inner inner = new Inner();
System.out.println(inner.value);
inner.say();
}
class Inner{
private String value = "DDD";
private String name = "内部类";
public void say(){
System.out.println(Outer.this.name);
System.out.println(name);
run();
}
}
}
创建内部类对象
显然成员内部类是寄生于外部类,创建内部类对象就必须先创造外部类对象。之后创建内部类有两种方式。
public class Test10 {
public static void main(String[] args) {
/*方式1创建成员内部类对象*/
Outer outer = new Outer();
Outer.Inner d = outer.new Inner();
/*方式2创建成员内部类对象*/
Outer.Inner d1 = new Outer().new Inner();
//Outer.Inner d1 = outer.getClassD();
}
}
class Outer{
private String name = "外部类";
public void run(){
System.out.println("外部类奔跑");
}
/*创建一个返回D对象的方法*/
public Inner getClassD(){
return new Inner();
}
/*使用内部类的属性和方法*/
public void eat(){
Inner d = new Inner();
System.out.println(d.value);
d.say();
}
class Inner{
private String value = "DDD";
private String name = "内部类";
public void say(){
System.out.println(C.this.name);
System.out.println(name);
run();
}
}
}
成员内部类的访问权限
成员内部类前可加上四种访问修饰符。
private:仅外部类可访问。
protected:同包下或继承类可访问。
default:同包下可访问。
public:所有类可访问。
局部内部类
局部内部类存在于方法中。
他和成员内部类的区别在于局部内部类的访问权限仅限于方法或作用域内。
class K{
public void say(){
class J{
}
}
}
注意事项:局部内部类就像局部变量一样,前面不能访问修饰符以及static修饰符。
匿名内部类
下面我们先通过一段代码初步了解一下匿名内部类。
public class T {
final static int num = 999;
public static void driveCar(Car car) {
car.drive();
}
public static void main(String[] args) {
T t = new T();
driveCar(new Car() {
@Override
public void drive() {
System.out.println("驾驶着BMW汽车 num="+num);
}
});
}
}
interface Car {
void drive();
}
分析以上代码知道静态方法driveCar需要一个Car对象,我们通过实现接口创建一个匿名类对象传递过去。事实上还可以通过继承类来创建一个匿名内部类对象。
匿名内部类和局部内部类只能访问外部类的final变量。
静态内部类
静态内部类和成员内部类相比多了一个static修饰符。它与类的静态成员变量一般,是不依赖于外部类的。同时静态内部类也有它的特殊性。因为外部类加载时只会加载静态域,所以静态内部类不能使用外部类的非静态变量与方法。
同时可以知道成员内部类里面是不能含静态属性或方法的。
class U {
static class I {
}
}
内部类的好处
- 完善了Java多继承机制,由于每一个内部类都可以独立的继承接口或类,所以无论外部类是否继承或实现了某个类或接口,对于内部类没有影响。
- 方便写事件驱动程序。
总结
public class Test15 {
public static void main(String[] args) {
//初始化bean1
Test15.Bean1 bean1 = new Test15().new Bean1();
bean1.i++;
//初始化bean2
Test15.Bean2 bean2 = new Test15.Bean2();
bean2.j++;
//初始化3
Bean bean = new Bean();
Bean.Bean3 bean3 = bean.new Bean3();
bean3.k++;
}
class Bean1 {
public int i = 0;
}
static class Bean2 {
public int j = 0;
}
}
class Bean {
class Bean3 {
public int k = 0;
}
}
静态内部类对象的创建一般是外部类.内部类 类名 = new 外部类.内部类();
成员内部类对象的创建一般是外部类.内部类 类名 = 外部类对象名.new 内部类();