前两天携程笔试,考到了内部类的一些知识点,已经有些模糊了,现在来通过《Java 编程思想》回顾一下
文章目录
- 1. 普通内部类:
- 内部类的创建
- 内部类获取外部类对象
- 2. 局部内部类
- 3. 匿名内部类
- 4. 嵌套类(静态内部类)
1. 普通内部类:
- 必须使用外部类的对象来创建该内部类对象。
- 内部类自动拥有对其外部类所有成员的访问权。
内部类的创建
在外部类的内部,可以直接的创建内部类对象,但如果是在外部类的静态方法,或别的类的方法中,就不能直接创建内部类对象,可以通过下面两种方法创建:
public class Parcel {
class Contents{
private int i =11;
public int value(){
return i;
}
}
public Contents getContents(){
return new Contents();
}
public static void main(String[] args) {
Parcel parcel1 = new Parcel();
Parcel.Contents contents1 = parcel1.new Contents();
Parcel parcel2 = new Parcel();
Parcel.Contents contents2 = parcel2.getContents();
}
}
第一种,通过外部类对象调用 .new 进行创建;
第二种,通过外部类中的非静态方法返回一个新建的内部类对象。
注:如果是别的类的方法需要使用内部类,需要具体的指出这个内部类的类型 OuterClassName.InnerClassName;如果是外部类的方法中使用,可以直接使用:InnerClassName,就像上面例子中的 main 方法,可以将 Parcel.Contents 直接换成 Contents。
内部类获取外部类对象
内部类对象会跟创建它的外部类对象有一种联系,所以它能访问对应的外部类对象的所有成员,不需要任何条件。通过 .this 实现
public class Parcel {
private int x = 5;
protected int y = 10;
public int z = 12;
public void f(){
System.out.println("调用f函数");
}
class Contents{
private int i =11;
public int value(){
return i;
}
public Parcel getOuterObject(){
Parcel parcel = Parcel.this;
System.out.println(parcel.x);
System.out.println(parcel.y);
System.out.println(parcel.z);
parcel.f();
return Parcel.this;
}
}
}
public class Test {
public static void main(String[] args) {
Parcel parcel1 = new Parcel();
Parcel.Contents contents1 = parcel1.new Contents();
Parcel parcel = contents1.getOuterObject();
// System.out.println(parcel.x); // 会因为访问权限报错
}
}
输出结果
5
10
12
调用f函数
2. 局部内部类
在一个方法里面或者在任意的作用域内定义内部类,一般有两个理由:
- 该内部类实现了某类型的接口,通过这个内部类对外部类的对象进行额外的操作。
- 解决一个复杂问题,需要创建一个类来辅助你的解决方案,但又不希望这个类是公共可见的。
下面是一个局部内部类的例子
public class Parcel5 {
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();
}
//TrackingSlip ts = new TrackingSlip("x"); //已经不在作用域范围,无法使用
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
p.track();
}
}
3. 匿名内部类
匿名内部类属于局部内部类中的一种,可以直接来看一个例子:
public class NoNameInnerClass {
class Contents{
private int i = 11;
public int value(){
return i;
}
public Contents(int x){
i = x;
System.out.println("创建Contents,x="+x);
}
public Contents(){
System.out.println("创建Contents");
}
}
public Contents contents(int x){
return new Contents(x){
private int x = 11;
public int value(){
return super.i + x;
}
};
}
public Contents contents(){
return new Contents(){
private int x = 11;
public int value(){
return super.i + x;
}
};
}
public static void main(String[] args) {
NoNameInnerClass noNameInnerClass = new NoNameInnerClass();
Contents c = noNameInnerClass.contents();
System.out.println(c.value());
}
}
//output:
//创建Contents
//创建Contents,x=5
//22
//16
contents 方法中,return new Contents(){}
这种语法的意思是,创建一个继承自 Contents 的匿名类的对象。上面的 contents 方法与下面的是等价的:
public Contents contents(){
class MyContents extends Contents{ //继承了Contents的局部内部类
private int i = 11;
public int value(){
return i;
}
}
return new MyContents();
}
需要注意,因为匿名类没有类名,因此他只有系统提供的默认无参构造器,创建匿名类对象时提供的参数,是提供给它的基类的构造器使用的,比如带参数 x 的 contents 方法。
如果希望匿名内部类能使用一个在其外部定义的对象,那么要求这个对象是 final 的,如下所示:
public Contents contents(final int t){
final int a = 6;
return new Contents(){
private int x = 11;
public int value(){
return super.i + x + a + t;
}
};
}
为什么需要 final 修饰,可以查看这篇文章:java匿名内部类使用外部变量时,外部变量必须是final,为什么?
注:jdk1.8 后已经可以不需要 final 修饰,但依旧无法在匿名内部类中对该对象进行修改。
PS:前面写的 Java 三种代理方式中的动态代理就有用到匿名内部类
4. 嵌套类(静态内部类)
嵌套类就是用 static 修饰的内部类,需要注意的是,嵌套类对象与外部类对象不存在任何联系,也就是说,嵌套类的创建不需要依赖于外部类对象,嵌套类无法访问外部类对象的非静态成员。它的用法与普通的类基本一致。
如下所示:
public class Parcel {
public int x = 5;
static int y = 10;
static class Contents{
private int i = 11;
public int value(){
// int sum = i + x; //x不是静态成员,无法访问
int sum = i + y;
return sum;
}
}
}
public class Test{
public static void main(String[] args) {
Parcel.Contents contents = new Parcel.Contents();
System.out.println(contents.value());
}
}