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

内部类作为外部类的成员,同样可以被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. 老李分享:走读unittest源码

    老李分享:走读unittest源码   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest测试开发工程师就业培训感兴趣 ...

  2. Android 用 camera2 API 自定义相机

    前言 笔者因为项目需要自定义相机,所以了解了一下 Android 关于 camera 这块的 API.Android SDK 21(LOLLIPOP) 开始已经弃用了之前的 Camera 类,提供了 ...

  3. Jmeter函数组件开发

    插件开发方法有两种: 一.在jmeter官网下载jmeter源码,在源码里面新加函数,然后导出jar: 二.不下载源码,直接导入jmeter相应的jar包,即可开发.(推荐) 下面介绍第二种开发方法: ...

  4. canvas画布

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. Masonry适配的简单使用

    一.Masonry是什么: 答:是一个很好的三方,用来做适配的 二.怎么使用Masonry 1.先导入头文件 #define MAS_SHORTHAND #define MAS_SHORTHAND_G ...

  6. koa-中间件流程控制

    koa中间件执行流程 koa中间件的的执行顺序是洋葱模型,外层逐步向内,执行到最中间再逐步向外扩展,实现这个顺序的模型需要依赖于generator函数,它可以暂停执行将控制权交出,等到执行next再得 ...

  7. Maven工程webinfo下面的JSP页面无法加载.js、.css文件的解决方案

    --下面是我的工程路径 --我jsp的写法 -----启动工程,访问js文件的路径是这样的, href="http://localhost:8080/activiti/css/public. ...

  8. IOS——触摸事件 视图检测和事件传递

    iPhone上有非常流畅的用户触摸交互体验,能检测各种手势:点击,滑动,放大缩小,旋转.大多数情况都是用UI*GestureRecognizer这样的手势对象来关联手势事件和手势处理函数.也有时候,会 ...

  9. angular中路由的实现(针对新手)

    最近搜了一下网上的教程,看完总感觉有点糊涂,对于新手来说可能云里雾里,所以写一个最简单的路由来给大家做个指引. 首先当然需要准备angular,下载地址:https://angular.io/ 现在a ...

  10. kafka入门

    1.Kafka独特设计在什么地方?2.Kafka如何搭建及创建topic.发送消息.消费消息?3.如何书写Kafka程序?4.数据传输的事务定义有哪三种?5.Kafka判断一个节点是否活着有哪两个条件 ...