`
hwfrog
  • 浏览: 3255 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

Java基础之接口和内部类

阅读更多

1、简介

1.1 接口

使用关键字:interface, 相较abstract的概念更加的迈进了一步:纯粹的抽象类。

有方法名、参数列表和返回类型, 但是没有任何的方法体。 隐含为public类型(也必须为public)。 也可以包含数据成员, 隐含为static final。 说白了, 接口只是提供了形式, 而没有提供任何具体的实现。

1.2 内部类

Inner Class似乎是一种代码掩藏机制: 将某些类放在另外的一个类的内部。 在归属上, 内部类依附于他的外围类。 Java的内部类需要同C++的嵌套类nested class和局部类local class进行区分。

1.3 C++嵌套类

在C++中, 一个类可以定义在另外的一个类中:嵌套类nested class。 嵌套类是外围类的一个成员, 可以出现在外围类的公有、私有或者保护区中。 嵌套类的名字在其外围类中是可见的, 但是在其他类域名字空间中是不可见的。

外围类充当了嵌套类命名空间的作用, 两者之间没有直接的联系, 嵌套类不能直接访问其外围类的非静态成员, 对外围类的非静态成员的访问都要求通过外围类的指针、引用或者对象来完成。这个同Java的内部类是存在差异的。

 

class List {
public :
    // ...
private:
    // 该嵌套类对外不可见, 因为是私有的
   class ListItem {
    public:
        ListItem (int val = 0);
        // ....
    };
};

// Implementation
List:ListItem:ListItem(int val)
{
    // ....
}


1.4 C++局部类

在C++中, 类也可以定义在函数的内部,这样的类称为局部类, local class, 局部类只在定义他的局部区域内可见。

区别于嵌套类,在定义该类的局部区域外没有语法能够应用局部类的成员, 由此局部类的成员还是必须定义在类的定义中, 同时不允许局部类声明静态数据成员, 因为没有方法进行初始化。

在实际的使用中, 很难找到一个理由不把局部类的所有成员都声明为公有的。另外, 局部类可以访问的外围域中的名字也是有限的, 局部类只能访问在外围域中定义的类型名、静态变量以及枚举值。

 

2、 接口

C++中,组合多个接口的行为称之为:多重继承。

Java中有所不同,可以执行相同的行为,但是只有一个类可以有具体的实现。 因此,可以通过组合接口来支持多继承(后面可以看到通过使用内部类可以变相的支持类似C++中的多继承)。

一个小小的问题:组合接口时,可能存在名字冲突, 也就是说, 在想要组合的不同接口中使用相同的方法名通常会造成代码可读性查(重载、实现和覆盖混合在仪器)。 一般尽量避免这种情况(也可以考虑使用内部类)。

1、通过继承扩展接口。通过继承在新接口添加新的方法声明。 也可以通过继承在新接口中组合多个接口。 这两种方法都可以获得新的接口。

注意一个语法上的变化:一般情况下, 我们之可以将extends用于一个单一的类。 但是既然接口可以由多个其他的接口产生, 那么在创建一个新的接口的时候, extends当然可以引用多个基类接口, 多个基类接口之间使用逗号进行分割。

interface NewInterface extnes InterfaceA, InterfaceB, InterfaceC
class NewClass implements InterfaceA, InterfacecB, InterfaceC
class NewClass extends BaseClass implements InterfaceA, InterfaceC

 

2、接口中任何数据成员自动都是static final的, 接口就成为了一种很便捷的用来创建常量的场所
既然接口中的数据都是static final的, 那么就需要进行初始化, 同时它们不能是‘空final’, 一般我们都是使用常量表达式来进行初始化, 但是也可以使用非常量表达式进行初始化。
3、接口可以嵌套
1)类中嵌套接口, 可以拥有public、包访问两种权限。 这两种接口都可以被实现为public、包访问权限以及private的内部类。
2)接口中嵌套接口, 只可以拥有public权限

3、内部类

内部类允许我们把一些逻辑相关的类组织在一起, 并控制在内部的类的范围内。 需要注意的一个要点就是:内部类与组合是两个不同的概念。

如何创建一个内部类:把类的定义放在外围类的里面就可以了:

public class SurroudingClass {
    class InnerClass {
        //....
    }
}

 

上面的定义并不复杂, 为什么需要内部类呢? 它的作用什么? 另外内部类同其他的类在具体的使用上存在哪些不同的呢?

3.1 内部类的分类

1、成员内部类

作为宿主类的一个成员存在, 和宿主类的属性、方法并列, 即类作用域

public class Outer {
    public class Inner {

    }
}

 

1) 内部类是一个编译时的概念, 一旦编译成功, 就会成为完全不同的两个类。 对于一个明为Outer的宿主类和其内部定义的Inner的内部类, 编译完成后会出现Outer.class和Outer$Inner.class两个类文件

2) 非静态内部类中不能定义静态成员, WHY? 参见后面的FAQ

 

2、本地内部类/局部内部类

在方法中定义的内部类成为本地内部类local inner class。 和局部变量类似。 在本地内部类前不能有访问限定符public或者private, 只能有abstract或者final。但是本地内部类可以访问当前代码块内的常量(final)和宿主类的所有成员, 其范围为定义他的代码块。 本地内部类的一个特例就是匿名内部类。

 

public class Example {
    public INTERFACE method() {
        class INTERFACE_IMP implements INTERFACE {
        }

        return new INTERFACE_IMP();
    }
}

 INTERFACE_IMP定义在method()中, 是一个本地内部类。

 

1)在宿主类的外面不能直接生成局部内部类的对象(保证局部内部类对外是不可见的)。 要想使用局部内部类, 需要生成宿主类的对象, 调用宿主类的方法, 在防范中才能使用局部内部类。通过内部类和接口达到一个强制的弱耦合, 用局部内部类来实现接口, 并在方法中返回接口的引用, 使得局部内部类对外不可见, 屏蔽了实现类的可见性。

2)如果局部内部类要访问这个方法的参数或者方法中定义的变量, 则这些参数或者变量必须被修饰为final。 为什么呢? 后面的FAQ中有详细的说明。

 

3、静态内部类

静态内部类定义在其他类的内部、方法的外面。 用static定义。 静态内部类中可以定义静态或者非静态成员, 有时候也成为嵌套类, 这个同C++中的嵌套类大致相似。 由于这个内部类为静态的。 因此就没有宿主类的对象引用。

1)要创建静态内部类的对象, 并不需要宿主类的对象

2)不能从静态内部类的对象中访问非静态的宿主类的对象成员(没有宿主类的对象引用)

那么静态内部类和非静态内部类有什么不同的地方呢?

1)和非静态内部类相比, 静态内部类没有了宿主类的对象的引用

2)在任何非静态内部类中, 不能定义静态数据成员、静态方法或者其他的静态内部类(内部类可以嵌套定义)。 不过静态内部类中却可以拥有这一切。

这里给出几个需要注意的地方:

1)静态内部类只能访问宿主类的静态成员和静态方法

2)静态内部类不能访问宿主类的非静态成员和方法, 因为它没有宿主类的对象引用

3)生成一个静态内部类的对象无需宿主类的对象。 静态内部类可以直接生成

class Outter {
    // static inner class
    static class StaticInner {
    }
}

Outter.StaticInner s = new Outter.StaticInner();

 4、匿名内部类

匿名内部类是一种特殊的局部内部类。通过匿名类来实现接口:

public interface InnerClass {
    int getI();
}

public class OutterClass{
    public InnerClass create() {
        return new InnerClass() {
            private int I= 11;
            public int getI() { return I; }
            };  // 这个分号是必须的, 表示了一个表达式的结束。 而不是类定义的结束  
    }

}

 等同于如下的代码

class MyInnerClass implements InnerClass {
    private int I = 11;
    public int getI() {
        return 1;
    }
}
return new MyInnerClass();

 1)匿名内部类是唯一一种没有构造方法的内部类

2)匿名内部类在编译的时候由系统自动起名为Out$1.class

 

3.2为什么需要内部类

典型的, 内部类继承某个类或者实现某个接口。 内部类的代码操作创建他的宿主类对象, 从这个角度看, 我们可以认为内部类提供了某种进入其宿主类的窗口。

总结了一下, 内部类的作用:

 1)隐藏你不想让别人知道的操作, 即封装性

 2)一个内部类对象可以访问创建它的宿主类对象的内容

 3)实现多继承

4)闭包和回调, 无需修改interface就能够实现同一个类中两宗同名方法的调用

1 封装性

类的访问权限即不可以是private, 也不可以是protected, 只有两个选择, 包访问权限或者public。 如果不希望任何人对一个类拥有访问权限, 可以把所有的构造器都设定为private。 但是这个限制对于内部类不适用。 一个内部类的访问权限可以是private或者protected, 这是一个特例。

内部类的封装主要体现在: 将内部类向上转型为基类, 尤其是接口, 这个接口的实现, 对外完全是不可见的, 并且不可用。 我们得到的不过是指向基类/接口的引用, 可用来隐藏实现的细节:

public interface AppInterface {
    String readOneLine();
}

 

public class Application {
    // 私有内部类, 对外隐藏细节, 除了Application本身, 没有人可以访问它
    private class AppImp implements AppInterface {
        private String strValue;
        private AppImp(String str) {
            strValue = str;
        }

        public String readOneLine() {
            return strValue;
        }
    }

    // 对外暴露接口, 而不是具体的实现, 向上转型
   public AppInterface app(String str) {
        return newAppImp(str);
    }

    public static void main(String[] args) {
        Application application = new Application();
        AppInterface appIf = application.app("Test");
        System.out.println("The value is" + appIf.readOneLine());
    }
}

 利用私有内部类, 为类的设计提供了一种杜绝“用具体类型来编码而引起的类型依赖性问题(type-coding dependencies)” , 并且完全将实现细节隐藏起来。 另外, 从使用者的角度看, 由于不能访问任何新增加、原本不属于公共接口的方法, 所以扩展接口是没有什么价值的。

2 一个内部类对象可以访问创建他的宿主类对象的内容

生成一个内部类的对象的时候, 它和他的宿主类对象之间就有了一种联系。 内部类对象能够访问其宿主类对象的所有成员, 包括私有成员,而无需任何特别的授权。 内部类拥有了宿主类所有元素的访问权。

区别于C++的嵌套类, 在C++中除非宿主类被声明为嵌套类的友元类, 否则它没有权利访问嵌套类的私有成员。 反之亦然。C++的嵌套类更多充当了命名空间的作用。

Java的内部类是如何做到访问宿主类的对象的内容的呢? 内部类里面肯定是要有一个指向‘负责创建它’的宿主类对象的引用。这样, 当你引用宿主类对象的成员的时候, 就使用这个引用来选去成员。 这个过程由编译器来实现。 很重要的一点是: 内部类对象的创建适合宿主类对象相关的, 创建内部类对象的前提就是要获取到宿主类对象的引用。 如果没有这个引用, 编译器将报告错误。 因此, 如果要创建一个pulibc inner class , 必须要先创建对应的宿主类对象才行:

public class Application {
    public class AppImp implements AppInterface {
        public String readOneLine()  {
            return new String("test");
        }
    }

    public static void main(String[] args) {
        Application application = new Application();
        //直接创建内部类对象, 必须使用宿主类对象来创建
      AppInterface appIf = application.new AppImp();
        System.out.println("The value is " + appIf.readOneLine());  
    }
}

 

3 实现多重继承

 Java只能扩展一个类,它的多重继承在内部类之前是通过实现多个接口来实现的, 但是使用接口有时候不是很方便, 比如实现一个接口就必须实现它里面的所有的方法。

内部类最为吸引人的一个地方就是: 每个内部类都可以独立的扩展某个实现,因此内部类不会受到‘宿主类是否已经扩展了某个实现’ 的约束。

内部类使得多继承的解决方案变的完整, 内部类提供了扩展多个实现或者abstract类的能力。通过内部类分别继承一个基类, 宿主类创建内部类的对象, 并使用内部类的方法, 变相的实现了多继承。

Java中的内部类和接口加在一起,也可以解决常被C++程序员抱怨的Java中存在的一个问题: 没有真正意义上的多继承。 实际上, C++的多继承设计起来很复杂,而Java通过内部类加上接口, 效果更好:

public class Mother {
    protected String character = new String("温柔");
}

 

public class Father {
    protected String character = new String(“强壮”);
}

 

public class Son {
    private class FromMother extends Mother {
        public String ch() {
            return character;
        }
    }

    private class FromFather extends Father {
        public String ch() {
            return character;
        }
    }

    public String character() {
        return new FromMother().ch() + new FromFather().ch();
    }

    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("My character is " + son.character());
    }
}

 Son分别实现了两个内部类FromMother和FromFather, 分别扩展了Mother和Father。 因此Son就拥有了Mother和Father的方法和属性, 也就间接的实现了多继承。 注意, Mother和Father在这里不是接口, 因此如果不使用内部类的话, 将无法实现多继承。

其实, 从上面的实现中可以看到, 使用内部类还是有不少的好处的。

1)在一个宿主类对象中, 我们可以创建内部类的多个实例, 这些实例都可以有自己的、和宿主类对象无关的状态信息

2)同理, 我们也可以在一个宿主类中实现一个接口或者同一个基类的不同的版本的实现, 这个十分有趣, 可以很好的解决代码的前向兼容的问题。

3)内部类对象创建的时机和宿主类对象的创建没有什么关系

4)内部类是一个独立的实体, 不存在‘is-a’ 的关系。 如果A extends B, 我们会说, A是一个B(钢琴是一个乐器), 但是使用内部类, 就不存在这个问题; 我们可以定义多个内部类的实例, 分别用于不同的目的, 内部类是独立的。

 

4、FAQ

4.1 为什么非静态内部类不能声明静态变量

因为内部类是非静态的, 除了要依赖宿主类对象的实例之外, 还要依赖内部类的实例, 而静态变量是不需要构建类的实例的, 这两者相互矛盾。

非静态内部类的对象脱离了宿主类的对象就不会存在, 静态变量的作用就是让该类的所有对象共享一个状态。 这个类的所有对象都可以获取和修改这个状态。 如果仅仅是这个目的, 就可以推出这个状态也是所有宿主对象所共享的状态, 因此, 这个定义就可以提升到宿主类中来定义, 而没有必要放在内部类中定义。 因此在Java中不允许在非静态内部类中声明静态变量。 但是允许其继承父类的静态变量, 因为父类可能有很多子类, 这些子类不一定都是内部类。

4.2 为什么匿名内部类和局部内部类只能访问final变量

这个是因为变量的作用域的问题。 因为匿名内部类是出现在一个方法的内部的, 如果它要访问这个方法的参数或者方法中定义的临时变量, 则这些参数和变量必须被修饰为final。 因为损然匿名内部类是在方法的内部定义的, 但是在实际编译的时候, 内部类编译成了outer.inner, 这说明内部类所处的位置和宿主类中的方法在同一个等级上, 宿主类中的方法中的变量或者参数只是方法的局部变量, 这些变量或者参数的作用域只在这个方法的内部有效。 因此编译的时候内部类和方法在同一个等级上, 所以方法中的变量或者参数只有为final, 内部类才可以使用。

但是为什么final就可以呢? 因为Java采用了一种copy local variable的方式来实现的, 也就是说把定义为final的局部变量拷贝过来用, 而引用也可以拿过来用, 只是不能重新赋值。 这样就造成了可以access local variable的假象, 而这个时候由于不能重新赋值, 所以一般不会砸成不可预料的事情发生。

4.3 内部类的继承

因此内部类的构造器要用刀宿主类的对象的引用, 在继承一个内部类的时候, 需要使用特欧的语法来明确说明这个事情, 也就是说要明确的给定一个宿主类的引用。

定义一个构造器(缺省的构造器无法工作), 传递一个指向宿主类对象的引用, 如下:

class Outer{
    class Inner{}
}

public class Example extneds Outer.Inner {
    Example(Outer ref) {
        ref.super();
    }
}

 

 

分享到:
评论
1 楼 hsabby 2013-08-19  
这也太详细了吧,多重继承这块之前真没好好想过呢,学习了

相关推荐

    Java__接口、内部类和Java_API基础

    "Java接口、内部类和Java_API基础" Java接口是Java语言中的一个抽象概念,它定义了一组方法的集合,但不提供实现。Java接口的主要特点是它不能被实例化,必须由其他类来实现。接口的声明格式为: ``` public ...

    Java 接口 内部类

    接口内部类是一种特殊的内部类,它定义在接口内部。这种类只有在接口中才能被声明,它提供了接口扩展的一种方式。接口内部类可以实现接口自身的抽象方法,也可以提供具体的实现。这种设计模式常用于事件监听器或者...

    接口、内部类和Java API基础

    接口(interface)是一组常量和抽象方法的集合。接口是一种引用数据类型。 抽象方法的具体实现由实现接口的类完成,实现接口的类必须覆盖接口中的所有抽象方法。

    2022年接口内部类和JavaAPI基础.ppt

    "2022年接口内部类和JavaAPI基础" 一、接口 1.1 声明接口 在Java中,接口是用来定义一组方法的集合,用于描述一个类的行为。声明接口使用关键字`interface`,例如: ```java public interface Area { public ...

    java期末复习抽象类与接口,内部类,知识点.zip

    复习时,可以详细阅读`Java抽象类.docx`来理解抽象类的定义、用途和实例化规则,通过`Java接口.docx`学习接口的定义、实现方式以及多继承的特点,`Java内部类.docx`将帮助你掌握不同类型的内部类及其应用场景。...

    java 集合和内部类资料

    Java集合框架是Java编程...总之,这个资料包涵盖了Java集合框架和内部类的基础知识,对于想要深入理解Java编程的开发者来说,是一份宝贵的学习资源。通过学习,你可以提升在处理数据集合和实现复杂设计模式上的能力。

    JAVA 教学 PPt(接口,类和对象尤其详尽)

    创建对象的过程称为实例化,使用关键字`new`和类的构造器完成。对象间的交互通过方法调用实现,这是面向对象编程中的消息传递。 4. 图形界面(GUI): Java提供了丰富的图形用户界面(GUI)工具包,如Java Swing和...

    java基础java内部类

    (1) 实现事件监听器:Java AWT 和 Swing 中的事件处理模型广泛使用内部类,创建匿名内部类实现监听器接口。 (2) 封装复杂逻辑:将特定功能封装到内部类,使外围类更简洁,降低耦合度。 (3) 实现线程:使用内部类创建...

    java 接口 java 接口java 接口

    Java接口在Java编程语言中扮演着至关重要的角色,它是实现多态性和抽象化的关键...理解和熟练使用接口是成为专业Java开发人员的基础。在实际项目中,正确地利用接口可以使代码结构清晰,易于维护,并提高代码的复用性。

    java基础 包含:Java入门 基础语法 JAVA 类和对象 JAVA 高级类 等等

    进入Java的高级主题,你会接触到诸如抽象类、接口、内部类、匿名类、枚举、注解、泛型、集合框架(如ArrayList、LinkedList、HashMap等)以及反射机制等。这些特性增强了代码的灵活性和可扩展性,使Java能够适应各种...

    接口,内部类,抽象类概念和区别

    总结,接口、内部类和抽象类是Java中重要的面向对象设计工具,它们各有特点,开发者应根据需求灵活选择和组合使用,以实现高效且易于维护的代码结构。在阅读和理解他人代码时,理解这些概念及其差异同样至关重要。

    Java基础权限控制与final,内部类

    了解了这些概念,让我们回到Java基础权限控制与final,内部类的主题。Java中,final和权限控制是面向对象编程中的核心概念。final关键字用于限制类、方法或变量的可变性,而权限控制则决定了类成员的可见性和可用...

    抽象类、接口、内部类、异常

    课后作业中的问题涉及到抽象类、接口、内部类的特点和区别,异常处理机制以及关键字`this`、`super`、`final`的用法,包机制,访问权限修饰符,接口实现与类继承的区别等Java基础知识。实践题要求设计实现接口、ATM...

    Java基础课件 全套pdf版 java培训

    - 内部类:理解匿名内部类、成员内部类和局部内部类。 - 静态与final关键字:掌握静态成员和常量的使用。 - 泛型:学习如何使用泛型提高代码的类型安全性和重用性。 4. **Java集合**(7. 尚硅谷_佟刚_Java基础_...

    java基础PPTJava基础PPT

    Java是一种广泛使用的...总的来说,这份"Java基础PPT"将带领学习者深入理解Java语言的基本概念和用法,为进一步的Java开发打下坚实的基础。通过细致的学习和实践,开发者可以熟练掌握这些知识,并运用到实际项目中去。

    java基础难点-更新版

    * 内部类可以继承外围类的成员变量和方法 * 内部类可以独立继承一个接口或类 * 内部类可以多实例,每个实例都有自己的状态信息 * 内部类提供了更好的封装,除了外围类,其他类都不能访问 3. 匿名内部类: * 匿名...

    Java基础尚硅谷宋红康学习笔记

    【Java基础】 Java是一种广泛使用的面向对象...这些是Java基础知识和高级技术的主要组成部分,掌握它们能够帮助开发者构建稳定、高效、可维护的Java应用程序。通过深入学习和实践,你可以不断提升自己的Java编程能力。

    Java基础练习代码

    3. **接口和内部类**:接口是一种定义行为的合约,允许类实现多个接口,实现多继承。内部类(嵌套类)分为成员内部类、局部内部类、匿名内部类和静态内部类,它们可以增加代码的封装性和复用性。 4. **面向对象的...

    Java基础知识整理

    Final关键字是Java语言中的一个保留字,用于定义不可变的变量、方法和类。 * Final数据:Final数据是指被final关键字修饰的变量,final变量的值不能被改变。 * Final参数:Final参数是指被final关键字修饰的方法...

Global site tag (gtag.js) - Google Analytics