【Java基础入门篇】三、面向对象和JVM底层分析(3)
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{
@Override
public void rest() {
}
@Override
public void run() {
}
}
class Cat extends Animal{
@Override
public void rest() {
}
@Override
public 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{
@Override
public 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{
// 实现接口的方法
@Override
public void helpOther() {
System.out.println("扶老奶奶过马路");
}
}
class Angel implements Volant{
// 实现接口的方法
@Override
public void fly() {
System.out.println("Angel.fly");
}
}
class HonestBird implements Honest, Volant{
@Override
public void fly() {
System.out.println("飞起来");
}
@Override
public 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{
@Override
public void testA() {
System.out.println("CImp01.testA");
}
@Override
public void testB() {
System.out.println("CImp01.testB");
}
@Override
public 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() {
// 在这里单独实现了一次接口内容
@Override
public void run() {
System.out.println("TestAnonymousClass.run");
}
});
t1.test1(new A() {
// 在这里单独实现了一次接口内容
@Override
public void run() {
System.out.println("第二次实现匿名内部类");
}
});
}
}
//定义一个类实现接口
class AImpl implements A{
@Override
public 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();
}
}