java面向对象--内部类
将一个类定义在另一个类里面,里面的那个类称为内部类,与属性、方法等一样视作外部类的成员。内部类提供了更好的封装,不允许同包中的其他类访问该内部类。
内部类作为外部类的成员,同样可以被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面向对象--内部类的更多相关文章
- JAVA面向对象-----内部类的概述
JAVA面向对象-–内部类的概述s 将类定义在另一个类的内部则成为内部类.其实就是类定义的位置发生了变化. 在一个类中,定义在类中的叫成员变量,定义在函数中的叫成员函数,那么根据类定义的位置也可以分为 ...
- Java面向对象㈢ -- 内部类
内部类必须要依赖于继承或实现一个接口.内部类可以实现Java多继承,内部类访问外表类的局部变量或参数时,则该局部变量或参数必须被final修饰.内部类不能包含有static的变量和方法,原因是因为内部 ...
- java 面向对象内部类
1.内部类概念:将一个类定义在另一个类的内部,该类就称为内部类 类中定义的内部类特点1) 内部类作为外部类的成员,可以直接访问外部类的成员(包括 private 成员),反之则不行.2) 内部类做为外 ...
- Java面向对象 Object类 内部类
Java面向对象 Object类 内部类 知识概要: 一:Object类 二:内部类 匿名内部类的写法 1.Object O ...
- 如何讲清楚 Java 面向对象的问题与知识?(类与对象,封装,继承,多态,接口,内部类...)
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- Java学习笔记:04面向对象-内部类_访问修饰符_final
04面向对象-内部类/访问修饰符/final 1.static的介绍 static:关键字,静态的 static的作用是用来修饰类中的成员 2.访问一个类中的某一个成员变量 方法一: _1.创建对象 ...
- Java面向对象程序设计--接口和内部类
1.接口的定义: In the Java programming language, an interface is not a class but staff[0] = ...
- Java面向对象的多态
Java中多态的概念是面向对象中除封装和继承外非常重要的知识点,也是Java面向对象三大特性最后一个特性 多态其实就是指对象存在的多种形态,多态分为引用多态和方法多态 引用多态的含义就是:父类的引用可 ...
- Java面向对象的封装
封装是Java面向对象的三大特性之一,通常我们是通过包管理机制同时对类进行封装,隐藏其内部实现细节,通常开发中不允许直接操作类中的成员属性,所以属性一般设置为私有权限private,类中一般会给出一些 ...
随机推荐
- Java并发编程:同步锁、读写锁
之前我们说过线程安全问题可以用锁机制来解决,即线程必要要先获得锁,之后才能进行其他操作.其实在 Java 的 API 中有这样一些锁类可以提供给我们使用,与其他对象作为锁相比,它们具有更强大的功能. ...
- es suggest did you mean资料
term suggester 功能介绍 term suggester 根据提供的文档提供搜索关键词的建议,也就是关键词自动纠错.该链接介绍如何使用 term suggester 语法.term sug ...
- C++学习笔记之模板篇
title: C++学习笔记之模板篇 tags: c++,c,模板,vector,friend,static,运算符重载,标准模板 --- 一.模板 不管是函数模板还是类模板,在未初始化前都是不占用内 ...
- Sphinx安装流程及配合PHP使用经验
1.什么是Sphinx Sphinx是俄罗斯人Andrew Aksyonoff开发的高性能全文搜索软件包,在GPL与商业协议双许可协议下发行. 全文检索式指以文档的全部文本信息作为检索对象的一种信息检 ...
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(六)easyUI与富文本编辑器UEditor整合
日常啰嗦 本来这一篇和接下来的几篇是打算讲一下JDBC和数据库优化的,但是最近很多朋友加我好友也讨论了一些问题,我发现大家似乎都是拿这个项目作为练手项目,作为脚手架来用的,因此呢,改变了一下思路,JD ...
- java 基础知识八 正则表达式
java 基础知识八 正则表达式 正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待 ...
- 关于苹果真机 getFullYear()返回值为NAN的问题
问题描述: 在html页面中获得后台传过来的一个时间并显示在页面上,我用getFullYear() ,getMonth(),getDate()分别获得了年月日在电脑上和三星手机上页面都能正确的显示时间 ...
- Unity3D 预备知识:C#与Lua相互调用
在使用Unity开发游戏以支持热更新的方案中,使用ULua是比较成熟的一种方案.那么,在使用ULua之前,我们必须先搞清楚,C#与Lua是怎样交互的了? 简单地说,c#调用lua, 是c# 通过Pin ...
- php基础知识(二)---2017-04-14
1.字符串的三种表达形式: (1)双引号 (2)单引号 (3)尖括号 $s = <<<A <div style="width:500px; height:100px; ...
- 聊聊 Tomcat 的单机多实例
Tomcat 从何而来? 先说 Tomcat 这一单词解释,如果你不是一个开发者,当然它在美国口语中并非是褒义词:如果你是开发者,那你一定听过 Web 应用服务器.Sun 公司和 Tomcat .如你 ...