将一个类定义在另一个类里面,里面的那个类称为内部类,与属性、方法等一样视作外部类的成员。内部类提供了更好的封装,不允许同包中的其他类访问该内部类。

内部类作为外部类的成员,同样可以被4个访问限定符修饰。如果外部类需要访问非静态内部类的成员,必须创建非静态内部类对象来访问。

内部类成员可以直接访问外部类的数据。

如果存在一个非静态内部类对象,则一定存在一个被它寄存的外部类对象,也就是说在拥有外部类对象之前是不可能创建内部类对象的。但外部类对象存在时,非静态内部类对象不一定存在。

非静态内部类里不能有静态方法/静态属性/静态初始化块,也不能有嵌套类(static innerClass)。

在外部类的非静态方法内,可以直接通过new创建内部类对象,而且可以省略OuterClassName。在外部类的非静态方法之外引用内部类的对象时,需要具体指明这个对象的类型OuterClassName.InnerClassName varName;而且创建时,需要由外部类对象调用内部类的构造器OuterInstance.new InnerConstructor()。

非静态内部类对象里对外部类对象的引用:OuterClassName.this,this指非静态内部类对象本身。

public class Outer {
private String prop = "outerPro";
public class Inner {
private String prop = "innerPro";
Inner() {
System.out.println(prop);
System.out.println(Outer.this.prop); // 内部类中对外部类对象的引用:OuterClassName.this
}
} public Inner getInner() {
return new Inner(); // 外部类的非静态方法内可以直接new创建内部对象
} public static void main(String[] args) {
Outer outerObject = new Outer();
Outer.Inner innerObject2 = outerObject.new Inner();// 外部类非静态方法中创建内部类必须通过instanceOfOuterClass.new创建
// Inner innerObject = outerObject.getInner();
}
}

内部类自动拥有对其外部类所有成员的访问权,包括private。

interface Selector {
boolean end();
Object current();
void next();
} public class Sequence {
private Object[] items;
private int next = 0;
public Sequence(int size) {
o = new Object[size];
}
public void add(Object x) {
if(next < o.length) {
o[next] = x;
next++;
}
}
private class SequenceSelector implements Selector {
int i = 0;
public boolean end() { //内部类可以直接访问外部类成员
return i == items.length;
}
public Object current() {
return items [i];
}
public void next() {
if(i < items.length) i++;
}
}
public Selector getSelector() {
return new SequenceSelector ();
} public static void main(String[] args) {
Sequence s = new Sequence(10);
for(int i = 0; i < 10; i++)
s.add(Integer.toString(i));
Selector sl = s.getSelector();
while(!sl.end()) {
System.out.println((String)sl.current());
sl.next();
}
}
}

在方法和作用域内的内部类

作用:

创建不是公用的类来辅助解决复杂问题;创建并返回实现了某类型接口的对象的引用。

定义在方法中:

public class Parcel5 {
public Destionation destionation(String str) {
class PDestionation implements Destionation { //方法里的内部类实现某个接口
private String label;
private PDestionation(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new PDestionation(str); //返回实现某个接口对象的引用
} public static void main(String[] args) {
Parcel5 parcel5 = new Parcel5();
Destionation d = parcel5.destionation("chenssy"); //向上造型
}
}

定义在作用域中:

public class Parcel6 {
private void internalTracking(boolean b) {
if (b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("chenssy");
String string = ts.getSlip();
}
}
public void track() {
internalTracking(true);
} public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}

匿名内部类

适合创建那种只需要使用一次的类。匿名内部类必须且只能继承一个父类或实现一个接口,语法如下,表示创建一个继承自某父类或实现某接口的匿名类对象。

  new 父类构造器(实参列表) | 接口() {

    //匿名内部类的实体部分

  };

创建匿名内部类时会立即创建一个该匿名类的实例,所以匿名内部类不能是抽象类。

匿名内部类因为没有类名,所以不能定义构造器,但可以定义实例初始化块进行初始化。

如果在匿名内部类里要使用一个在其外部定义的变量,编译器要求其参数引用是final的。

public class Parcel10 {
public Destination destination(final String dest, final float price) {//参数引用必须是final的
return new Destination() {
private int cost;
// 只能通过初始化块对匿名内部类初始化
{
cost = Math.round(price);
if (cost > 100)
System.out.println("Over budget!");
}
private String label = dest;
public String readLabel() {
return label;
}
};
}
public static void main(String[] args) {
Parcel10 p = new Parcel10();
Destination d = p.destination("Tasmania", 101.395F);
}
}

工厂方法:改进接口中的工厂方法

interface Service {
void method1();
void method2();
} interface ServiceFactory {
Service getService();
} class Implementation1 implements Service {
private Implementation1() {
}
public void method1() {
System.out.println("Implementation1 method1");
}
public void method2() {
System.out.println("Implementation1 method2");
}
public static ServiceFactory factory = new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
};
} class Implementation2 implements Service {
private Implementation2() {
}
public void method1() {
System.out.println("Implementation2 method1");
}
public void method2() {
System.out.println("Implementation2 method2");
}
public static ServiceFactory factory = new ServiceFactory() {//通过匿名内部类生成一个factory对象
public Service getService() {
return new Implementation2();
}
};
} public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
} public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
// Implementations are completely interchangeable:
serviceConsumer(Implementation2.factory);
}
}

静态内部类:被static修饰的内部类。

static关键字把类的成员变成类相关,不再是实例相关。外部类不能被static限定,因为外部类上一级是包不是类。

静态内部类的对象不是寄存在外部类对象中,而是寄存在外部类的类本身中。静态内部类对象只有外部类的类引用,没有外部类对象的引用。也就是创建静态内部类的对象时,不需要外部类对象,直接通过外部类调用内部类的构造器new OuterClass.InnerContructor()。

因为没有外部类对象的引用,所以不能从静态内部类对象的方法中访问非静态的外部类对象的实例属性,类似于static方法。

普通内部类因为需要有外部类对象,会和编译器加载静态资源的顺序冲突,所以不能有static数据,不能包含静态内部类。但是静态内部类可以包含静态成员,也可以包含非静态成员。外部类不能直接访问静态内部类的成员,需要使用静态内部类的类名访问内部类的类成员,静态内部类的对象访问内部类的实例成员。

接口中的内部类被public和static修饰,只能是静态内部类。

为什么需要内部类?

内部类使得多重继承的解决方案更完美。如果继承的是多个类(抽象或具体)而不是接口,则只能使用内部类实现多重继承。

class D {
}
class E {
}
class Z extends D {
E makeE() {
return new E() {
};
}
} public class MultiImplementation {
static void takesD(D d) {
}
static void takesE(E e) {
}
public static void main(String[] args) {
Z z = new Z();
takesD(z);
takesE(z.makeE());
}

闭包和回调

闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域的信息。Java并无闭包规范,而是通过“接口+内部类”来实现闭包的功能。因为对于非静态内部类而言,它不仅记录了其外部类的详细信息,还保留了一个创建非静态内部类对象的引用,通过这个引用可以访问外部类的所有成员,所以可以把非静态内部类对象当成面向对象领域的闭包。

某个方法一旦获得了内部类对象的引用后,就可以在需要时调用外部类实例的方法。

例如Teachable接口和Programmer父类,都有work方法,但程序员和教师的work功能不一样,如何实现一个类同时具备不同功能的work()方法:

interface Teachable {
void work();
} class Programmer {
private String name;
public Programmer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} public void work() {
System.out.println(name + "正在敲代码...");
}
} class ComputerTeacher extends Programmer {
public ComputerTeacher(String name) {
super(name);
}
//程序员工作使用父类Programmer实现的work()方法
//教学工作仍定义在ComputerTeacher中,但是与Teachable接口没有关系,而且是private的,本类之外不能访问
private void teach() {
System.out.println(getName() + "正在讲课...");
}
//非静态内部类实现Teachable的work方法,作用仅仅是向客户类提供一个调用外部类方法的途径
private class Closure implements Teachable {//实现了Teachable接口,可以当作Teachable使用
public void work() {
teach();
}
}
// 返回一个非静态内部类的引用,允许外部类通过该引用来回调外部类的方法
public Teachable getCallbackReference() {
return new Closure();
}
} public class TestClosure {
public static void main(String[] args) {
ComputerTeacher ct = new ComputerTeacher("程序猿");
// 直接调用ComputerTeacher从Programmer类继承下来的work方法
ct.work();
// 表明上看是调用的Closure的work方法,实际上是通过work方法调用ComputerTeacher的teach方法
ct.getCallbackReference().work();
}
}

内部类标识符

每个类都会生成一个.class文件,包含了如何创建这个类型的对象有关的所有信息(此信息产生了一个名为Class对象的“meta-class”)。内部类也如此,内部类class文件命名规则:

  OuterClassName1&OuterClassName2&InnerClassName.class

如果内部类是匿名的,那么编译器会简单地生成数字,把它们作为内部类标识符使用。

java面向对象--内部类的更多相关文章

  1. JAVA面向对象-----内部类的概述

    JAVA面向对象-–内部类的概述s 将类定义在另一个类的内部则成为内部类.其实就是类定义的位置发生了变化. 在一个类中,定义在类中的叫成员变量,定义在函数中的叫成员函数,那么根据类定义的位置也可以分为 ...

  2. Java面向对象㈢ -- 内部类

    内部类必须要依赖于继承或实现一个接口.内部类可以实现Java多继承,内部类访问外表类的局部变量或参数时,则该局部变量或参数必须被final修饰.内部类不能包含有static的变量和方法,原因是因为内部 ...

  3. java 面向对象内部类

    1.内部类概念:将一个类定义在另一个类的内部,该类就称为内部类 类中定义的内部类特点1) 内部类作为外部类的成员,可以直接访问外部类的成员(包括 private 成员),反之则不行.2) 内部类做为外 ...

  4. Java面向对象 Object类 内部类

     Java面向对象 Object类    内部类 知识概要:                 一:Object类                 二:内部类 匿名内部类的写法 1.Object O ...

  5. 如何讲清楚 Java 面向对象的问题与知识?(类与对象,封装,继承,多态,接口,内部类...)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  6. Java学习笔记:04面向对象-内部类_访问修饰符_final

    04面向对象-内部类/访问修饰符/final 1.static的介绍 static:关键字,静态的 static的作用是用来修饰类中的成员 2.访问一个类中的某一个成员变量 方法一: _1.创建对象 ...

  7. Java面向对象程序设计--接口和内部类

    1.接口的定义: In the Java programming language, an interface is not a class but          staff[0] =       ...

  8. Java面向对象的多态

    Java中多态的概念是面向对象中除封装和继承外非常重要的知识点,也是Java面向对象三大特性最后一个特性 多态其实就是指对象存在的多种形态,多态分为引用多态和方法多态 引用多态的含义就是:父类的引用可 ...

  9. Java面向对象的封装

    封装是Java面向对象的三大特性之一,通常我们是通过包管理机制同时对类进行封装,隐藏其内部实现细节,通常开发中不允许直接操作类中的成员属性,所以属性一般设置为私有权限private,类中一般会给出一些 ...

随机推荐

  1. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列   事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些 ...

  2. Mybatis基础学习(五)—缓存

    一.概述      mybatis提供查询缓存,如果缓存中有数据就不用从数据库中获取,用于减轻数据压力,提高系统性能.           一级缓存是SqlSession级别的缓存.在操作数据库时需要 ...

  3. Git基本操作命令

    先配置用户和邮箱: Administrator@USER-20150302NL MINGW32 ~$ git config --global user.name "youname" ...

  4. iOS 滑动页面标题切换颜色渐变效果

    话不多说,直接上图,要实现类似如下效果. 这个效果非常常见,这里着重讲讲核心功能 封装顶部的PageTitleView 封装构造函数 封装构造函数,让别人在创建对象时,就传入其实需要显示的内容 fra ...

  5. JS滚动加载

    var one = true;//设置一个全局变量 $(window).scroll(function () { var hight = document.body.scrollHeight - do ...

  6. ios deprecated 警告消除 强迫症的选择

    #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" ...

  7. .NET程序员也学Node.js——初识Node.js

    清明在石门休了八天假,一眨眼,4月又到中旬了...看到.NET在天朝彻底沦陷而又无能为力,我开始尝试去学习一些新的东西来充实自己,我自然是打死不会去学java的,没有为什么,于是乎,最近开始学习一些前 ...

  8. PAT 1057

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  9. Spring+SpringMVC+MyBatis+easyUI整合优化篇(十)数据层优化-整合druid

    druid介绍 这是druid对自己的介绍: Druid是阿里开源的一个数据库连接池技术,号称自己是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.B ...

  10. 【HDOJ 1286】找新朋友

    找新朋友 Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submissi ...