一个定义在另一个类中的类,叫作内部类。
- 创建内部类:如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,必须具体指明这个对象的类型:
OuterClassName.InnerClassName
内部类具有外部类所有元素的访问权。
(实现: 内部类对象秘密捕获一个指向那个外部类对象的引用)
使用内部类的原因
既然外部类也能实现接口,为什么要用内部类呢?
每个内部类都能独立地继承自一个接口的实现,所以无论外部类是否已经继承了某个接口实现,对于内部类都没有影响。
推荐阅读:java内部类的四大作用
实际上:Java通过内部类加上接口,可以很好的实现多继承。
- 封装性
- 访问外部类对象
- 实现多重继承
- 通过匿名内部类优化简单的接口实现
这里主要讲多继承实现:**
public class ExampleOne {
public String name() {
return "inner";
}
}
public class ExampleTwo {
public int age() {
return 25;
}
}
public class MainExample {
//内部类1继承ExampleOne
private class InnerOne extends ExampleOne {
public String name() {
return super.name();
}
}
//内部类2继承ExampleTwo
private class InnerTwo extends ExampleTwo {
public int age() {
return super.age();
}
}
在MainExample中 通过内部类 实现了对于两个class的继承。
.this 和 .new
使用方法:
return OuterClassName.this; //.this用法
//返回当前外部对象引用
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
} //.new用法
//想要直接创建内部类对象,必须使用外部类对象来创建该内部类对象
内部类与向上转型
推荐阅读:[Java的内部类与向上转型]
使用原因:由于Java的内部类的定义很多时候为 private
或 protected
的,为了获取内部类对象,我们会借助向上转型为借口的方式实现。
为何不直接实现呢?例如:
public class PizzaStore {
private class SausagePizza{
int size;
private SausagePizza(int size) {this.size = size;}
public void getName() {System.out.println("Got a SausagePizza of size:\t" + size);}
public int getSize() {return size;}
}
public SausagePizza getSausagePizza(int size) {
return new SausagePizza(size);
}
}
//我们直接从test开始看,这里的本意是利用 PizzaStore的外部类创建一个SausagePizza,
//但是由于SausagePizza为内部类,其类声明对其他类均为不可见,所以无法创建对象
public class Test {
public static void main(String[] args) {
PizzaStore store = new PizzaStore();
PizzaStore.SausagePizza pizza = store.getSausagePizza(5);
}
}
解决方案: 使用内部类实现接口完成,但要注意,若要调用实例中的方法,该方法必须在接口中实现。
public interface Pizza {
public void getName();
}
public class PizzaStore {
private class CheesePizza implements Pizza {
private int size;
private CheesePizza(int size) {this.size = size;}
public void getName() {System.out.println("Got a CheesePizza of size:\t" + size);}
public int getSize() {return size;} //注意,接口中未声明getsize,故无法调用该方法
}
......
内部类方法和作用域
使用原因:
- 实现某类型接口,创建并返回对其的引用
- 解决复杂问题,创建一个类辅助解决,但又不希望将类公开
代码:
(1)定义在方法中
public class Parcel5 {
public Destination destination(String s) {
final class PDestination implements Destination { //直接看这儿
private String label; //该类定义在destination方法中
//局部内部类
private PDestination(String whereTo) {
label = whereTo;
}
@Override
public String readLabel() { return label; }
}
return new PDestination(s);
}
(2)定义在任意作用域
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("slip");
String s = ts.getSlip();
}
// Can't use it here! Out of scope:
//- TrackingSlip ts = new TrackingSlip("x");
}
只能在if的作用域内可用。
匿名内部类
直接从代码理解ba
public interface Contents {
int value();
}
public class Parcel7 {
public Contents contents(int x) { //带参构造器,并进行初始化
return new Contents(int x) { // Insert class definition [1]
private int i = x;
@Override
public int value() { return i; }
}; // Semicolon required,此处的分号并不是标记内部类结束,而是标记表达式结束
} //表示创建一个继承自 Contens 匿名类的对象,被自动向上转型为对Contents的引用
}
//给出一般定义对比
public class Parcel7b {
class MyContents implements Contents { [2]
private int i;
Mycontens(int x){i=x;}
@Override
public int value() { return i; }
}
public Contents contents() {
return new MyContents();
}
}
匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。
嵌套类
创建前提:
- 要创建嵌套类的对象,并不需要其外部类的对象。
- 不能从嵌套类的对象中访问非静态的外部类对象。(因为没有保存外部类对象引用
创建方法:
public class Parcel11 {
private static class ParcelContents implements Contents { //直接加上static
public static x = 10; //[1]
private int i = 11;
@Override
public int value() { return i; }
}
}
注意: [1] 普通内部类不能够有static数据,而嵌套类可以。
接口内部的类:
- 接口内部的类自动定义为 public 和 static
- 可以在类中创建一个test类用于测试这个类。
public class TestBed {
public void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) {
TestBed t = new TestBed();
t.f();
}
}
}
闭包与回调
概念:
(1) 闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。
通俗的讲:闭包可以理解为一个代码片段的引用,在C语言中代码片段可以理解为函数指针,传代码片段的方法,称为闭包。
(2) 回调:
举例:A让B做一件事情,一种不停的去问B做完了没有。另一种就是A写一个接口,让B做完了去调用这个接口,即B做完了后去调用这个接口。然后A只需要实现这个接口,而这个接口的实现代码里就是B做完了的事情
一个经典例子让你彻彻底底理解java回调机制
(3) 意义:在运行时动态地决定需要什么方法。例如在图形界面实现GUI功能。分析见上。
内部类与控制框架 *
应用:解决响应事件的需求(GUI)。
import java.util.*;
public class Controller {
// A class from java.util to hold Event objects:
private List<Event> eventList = new ArrayList<>();
public void addEvent(Event c) { eventList.add(c); }
public void run() {
while(eventList.size() > 0)
// Make a copy so you're not modifying the list
// while you're selecting the elements in it:
for(Event e : new ArrayList<>(eventList))
if(e.ready()) {
System.out.println(e);
e.action();
eventList.remove(e);
}
}
}
来看这段代码,List中存储的是Event事件,所有的执行判断由Event本身进行判断。从 ready()
判断,到 action()
。使变化的事物与不变的事物相互分离。
通过内部类完成各类event的Override.
有意思。
继承内部类
难点:内部构造器必须连接到指向其外部类对象的引用。
解决方案:用特殊语法显示指出关联。
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){ //此处不能够利用默认构造函数
wi.super(); //要显示给出外部类初始化
}
....
}
内部类可以被覆盖吗?
可以。覆盖前和后的两个内部类是完全独立的两个实体,各自在自己的命名空间内。但是可以明确是某个内部类(直接赋值。