Java基础入门篇
三、面向对象和JVM底层分析
3.5 抽象类/接口和外/内部类
(1)抽象类
**“抽象方法”**是使用abstract
修饰的方法,没有方法体,只有声明,定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
**“抽象类”**是包含抽象方法的类,通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
抽象类的使用要点:
- 有抽象方法的类只能定义成抽象类
- 抽象类不能实例化(不能new)
- 抽象类可以包含属性、方法、构造方法,但是构造方法不能用来new实例,只能用来被子类调用
- 抽象类只能用来被继承
- 抽象方法必须被子类实现
package BasicJava.abstractClass;
/*
* 测试抽象类*/
public abstract class Animal {int age;public abstract void rest();public abstract void run();public void shout(){System.out.println("Animal.shout");}
}// 子类必须实现父类的抽象方法,否则会报错
class Dog extends Animal{@Overridepublic void rest() {}@Overridepublic void run() {}
}class Cat extends Animal{@Overridepublic void rest() {}@Overridepublic void run() {}
}
(2)接口
**“接口”**是一组规范,是所有类都要遵守的,面相对象的精髓就是接口,接口是比抽象类还抽象的“抽象类”,可以更加规范对子类进行约束,接口全面地专业地实现了:规范和具体实现的分离,在接口中只能有抽象的方法,不可以包含普通的属性和方法。
“接口”和实现类不是父子关系,是实现规则
的关系,是多个模块之间通信的标准,做系统往往就是“面相接口”的思想来设计
“接口”定义:
- 访问修饰符只能是public或者默认
- 接口名和类名采用相同的命名机制
- extends部分,接口可以多继承
- 接口中的属性只能是常量,即用public static final修饰,不写默认也是
- 接口中的方法只能是public abstract,省略也是
子类通过implements
来实现接口中的规范,接口不能创建实例,但是可以用于声明引用变量类型。一个类实现类接口,必须实现接口中的所有方法,并且这些方法只能是public的。
- JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
- JDK1.8(含8)之后,接口中包含普通的静态方法、默认方法。
接口中定义静态方法和默认方法(JDK≥8),JDK8之前接口中只能有抽象方法
**默认方法:**使用default关键字,默认方法和抽象方法的区别是抽象方法必须被实现,默认方法不是,接口可以提供默认方法的实现,所有接口的实现类都可以得到默认方法,也可以重写
**静态方法:**在接口中定义静态方法,这个静态方法直接从属于接口(接口也是一种特殊的类),可以通过接口名调用。如果子类定义了相同名字的静态方法,那就是完全不同的方法,子类中定义的静态方法直接从属于子类,可以通过子类名直接调用
注意:默认方法相当于普通方法,可以调用静态方法,但是静态方法无法调用默认方法
package BasicJava.testInterface;public interface TestDefault {void printInfo();// 定义默认方法default void moren(){System.out.println("TestDefault.moren");System.out.println("这是一个默认方法");// 默认方法可以调用静态方法testStatic();}// 定义静态方法static void testStatic(){System.out.println("TestDefault.testStatic");}}class Test01 implements TestDefault{@Overridepublic void printInfo() {System.out.println("Test01.printInfo");}public static void testStatic(){System.out.println("Test01.testStatic");}
}
package BasicJava.testInterface;// 飞行接口
public interface Volant {/*public static final*/ int Fly_HEIGHT = 100;/*public abstract*/ void fly();
}// 善良接口
interface Honest{void helpOther();
}class GoodMan implements Honest{// 实现接口的方法@Overridepublic void helpOther() {System.out.println("扶老奶奶过马路");}
}class Angel implements Volant{// 实现接口的方法@Overridepublic void fly() {System.out.println("Angel.fly");}
}class HonestBird implements Honest, Volant{@Overridepublic void fly() {System.out.println("飞起来");}@Overridepublic void helpOther() {System.out.println("日行一善");}
}
编写主函数测试接口:
package BasicJava.testInterface;/*
* 测试接口*/
public class Test {public static void main(String[] args) {Angel a = new Angel();a.fly();HonestBird hb = new HonestBird();hb.fly();hb.helpOther();// 访问接口的常量System.out.println(Volant.Fly_HEIGHT);// 实现一个向上转型,只能使用编译类型中有的方法Volant a2 = new HonestBird();a2.fly();// 通过强制类型转换为,向下转型,使用更多的方法HonestBird hb2 = (HonestBird)a2;hb2.helpOther();// 测试默认方法System.out.println("====测试默认方法===");Test01 td = new Test01();td.moren();// 测试静态方法System.out.println("====测试静态方法===");TestDefault.testStatic(); // 直接调用接口中的静态方法Test01.testStatic(); // 不是接口的静态方法实现,相当于一个全新的方法}}
接口支持**“多继承”**,和类的继承类似,子接口会继承父接口的一切。
package BasicJava.testInterface;
/*
* 测试接口多继承*/
public class TestMutipleInheritance {public static void main(String[] args) {C c = new CImp01();c.testA();c.testB();c.testC();}}interface A{void testA();
}interface B{void testB();
}// C接口同时继承A,B两个接口
interface C extends A, B{void testC();
}// 实现接口C之后,需要把C继承的接口的抽象方法也实现
class CImp01 implements C{@Overridepublic void testA() {System.out.println("CImp01.testA");}@Overridepublic void testB() {System.out.println("CImp01.testB");}@Overridepublic void testC() {System.out.println("CImp01.testC");}
}
(3)外/内部类
**内部类:**我们把一个类放在另一个类内部定义,称为内部类(inner class),分为:
- 成员内部类(静态内部类、非静态内部类)
- 匿名内部类
- 局部内部类
内部类:1.提供了更好的封装,只能让外部类直接访问,不允许同一个包中的其他类直接访问,2.内部类可以直接访问外部类的私有属性,内部类被当做其外部类的成员,但是外部类不能访问内部类的内部属性
- 内部类只是**“编译”**时的一个概念,一旦变异成功,就会成为完全不同的两个类,对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。
- 编译完成后会出现Outer.class 和 Outer.Inner.class两个类的字节码文件,所以内部类是相对独立的一种存在,其成员变量/方法名可以与外部类相同
非静态内部类:(外部类里使用非静态内部类和平时使用其他类没有什么不同):1.非静态内部类对象必须寄存在一个外部类对象里,因此,如果有一个非静态内部类对象,那么一定存在对应的外部类对象,非静态内部类对象单独属于外部类的某个对象, 2.非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员,3.非静态内部类不能有静态方法/属性/初始化块
静态内部类:,定义方式static class ClassName{//类体},1.静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员,2.静态内部类看作是外部类的一个静态成员
在内部类中进行成员变量访问要点:1.内部类属性:this.变量名,2.外部类属性:外部类名.this.变量名
package BasicJava.TestInnerClass;
/*
* 测试内部类的用法*/
public class Outer {private int age = 10;private static int b = 100;public void show(){System.out.println("Outer.show");System.out.println(age);}// 定义非静态内部类public class Inner{int age = 20;public void show(){System.out.println("Inner.show");System.out.println(age);// 访问外部类属性和方法System.out.println(Outer.this.age);Outer.this.show();}}// 定义静态内部类static class Inner2{public void test(){System.out.println(b); // 调用外部类的静态成员// System.out.println(age); // 不能调用外部类的非静态成员}}}
外部类定义内部类new Inner(),外部类以外的地方使用非静态内部类
:Outer.Inner n = new Outer().new Inner();
外部类以外的地方使用静态内部类
:Outer.Inner n = new Outer.Inner();
package BasicJava.TestInnerClass;
/*
* 测试创建内部类对象*/
public class TestInnerClass {public static void main(String[] args) {// 创建同一个包下的外部类对象Outer o = new Outer();o.show();// 创建同一个包下的非静态内部类对象Outer.Inner oi = new Outer().new Inner();oi.show();// 创建同一个包下的静态内部类对象Outer.Inner2 oi2 = new Outer.Inner2();oi2.test();}
}
匿名内部类:适合那种只需要使用一次的类,比如:键盘监听操作等,在“安卓开发”,awt、swing开发(窗口GUI开发)中常见,语法:new 父类构造器(实参类表) \实现接口 (){//类体}
package BasicJava.TestInnerClass;
/*
* 测试匿名内部类*/
public class TestAnonymousClass {public void test1(A a){a.run();}public static void main(String[] args) {TestAnonymousClass t1 = new TestAnonymousClass();// 原来的做法,首先额外定义一个类实现接口,再调用t1.test1(new AImpl());//匿名类的用法t1.test1(new A() {// 在这里单独实现了一次接口内容@Overridepublic void run() {System.out.println("TestAnonymousClass.run");}});t1.test1(new A() {// 在这里单独实现了一次接口内容@Overridepublic void run() {System.out.println("第二次实现匿名内部类");}});}
}//定义一个类实现接口
class AImpl implements A{@Overridepublic void run() {System.out.println("跑!");}
}
// 定一个接口
interface A{void run();
}
**局部内部类:**定义在方法内部,作用域只限于本方法,成为局部内部类,在实际开发中应用较少
package BasicJava.TestInnerClass;
/*
* 测试局部内部类*/
public class TestLocalClass {public void show(){// 局部内部类作用域仅限于该方法class Inner3{public void fun(){System.out.println("Hello world!");}}new Inner3().fun();}public static void main(String[] args) {new TestLocalClass().show();}}