0%

内部类的定义与分类

内部类的定义与分类

在《Thinking in Java》这本书里面给出的定义是:可以将一个类的定义放在另一个类的定义内部就是内部类。用一段代码来说明就是下面这样的

1
2
3
>class Outter{
>class InnerClass{}
>}

其中InnerClass就是内部类。

内部类大体上可以划分为以下几种:成员内部类,局部内部类,静态内部类,匿名内部类。下面我将依次讲解这几种内部类。

成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,即使是private属性成员和方法也可以访问。但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意的几点:

第一:成员内部类中不能存在任何static的变量和方法;

第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

第三:成员内部类也是有包的权限的,如果是public,则在任何一个包下面都可以使用,没有权限修饰符的则

​ 只能在当前包下面使用。private则只能在当前类中使用,protected则可以在子类中使用。

下面我们来看一个案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class OuterClass {
private String str;

public void outerDisplay(){
System.out.println("outerClass...");
}

public class InnerClass{
public void innerDisplay(){
//使用外围内的属性
str = "chenssy...";
System.out.println(str);
//使用外围内的方法
outerDisplay();
}
}

/*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
public InnerClass getInnerClass(){
return new InnerClass();
}

public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
--------------------
chenssy...
outerClass...

.this 和 .new的使用

如果你需要在内部类生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this。这样产生的引用自动地具有正确的类型,这一点在编译期就被知晓并受到检查,因此没有任何运行时开销。

.this使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
>public class OutterClass {


private int key = 0;
private int key2 = 0;


public static void main(String[] args) {
OutterClass k1 = new OutterClass();
InnerClass in = k1.new InnerClass(); //生成内部类
OutterClass k2 = in.getOutterClass(); //通过 .this 来生成外部类
System.out.println(k1 == k2); //true 说明俩个对象指向同一个引用
}

public int getKey() {
return this.key;
}

public class InnerClass {

public InnerClass() {
key = 12;
key2 = 13;
}

public int getKey() {
// 通过 .this 来使用外部类的getKey
return OutterClass.this.getKey();
}
// 使用 .this 返回外部类对象
public OutterClass getOutterClass() {
return OutterClass.this;
}
}
>}
>---------------
>outPut:
>true

这个例子演示了**.this的使用,同时也证明了.this**返回的就是外部类生成的对象。

**.new **使用

如果希望创建内部类对象,则必须先拥有外部类对象前提下才能创建,这是因为内部类对象会暗暗地连接到创建它的外部类对象上。这里就先不仔细说明,等看过例子之后在来说明好理解一些。创建内部类的格式是

1
outterObject.new    outterclass.innerClass

也就是外部类的对象后面跟上圆点和new。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class OuterClass {
private String name ;
private int age;

/**省略getter和setter方法**/

public class InnerClass{
public InnerClass(){
name = "chenssy";
age = 23;
}

public void display(){
System.out.println("name:" + getName() +" ;age:" + getAge());
}
}

public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.display();
}
}
--------------
Output:
name:chenssy ;age:23

通过上面的例子你大概能明白**.this.new**的作用。

通过上面的介绍相信你对成员内部类有一定的了解了,但肯定也会有一些以后,为什么内部类会自动拥有对其外部类所有成员的访问权。这是如何做到的呢?当某个外部类创建完一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后,当你访问此外围类的成员时,就是用那个引用来选择外围类的成员。幸运的是,编译器会帮你处理所有的细节。

局部内部类

其实这个名字不是太好,但是又不知道起什么名字。这一部分讲解的类主要是在方法和作用域内的内部类。在一个方法里面或者任意的作用域内定义内部类的理由有俩个:

  1. 实现了某类型的接口,于是可以创建并返回对其的引用。
  2. 你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但又不希望这个类是公共可用的。

下面使用来个具体的例子来讲解,因为这一部分没有什么好讲解的,主要是自己体会。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>public class Parcel5{
private int i= 0;
public Destination destination(String s ){
class PDestination implements Destination{
private String label;
private PDestination(Strng whereTo){
label = whereTo;
}
public String readLable(){
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args){
Parcel5 p =new Parcel5();
Destination d = p.destination("fuyang");
}
>}

上面的代码演示的是定义在方法中的内部类,不过在这里有一点需要注意的是,这个内部类的范围是在方法内。也就是他的作用域就在方法内部。所以方法外面的属性是访问不到的,但是可以访问方法内的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>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();
}

// 到这里就不能使用上面定义的TrackingSlip内部类了。
//下面这句话就会编译报错
// TrackingSlip ts = new TrackingSlip("chenssy");
}

public void track(){
internalTracking(true);
}

public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
>}

静态内部类

使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:

1、 它的创建是不需要依赖于外围类的。

2、 它不能使用任何外围类的非static成员变量和方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class OuterClass {
private String sex;
public static String name = "chenssy";

/**
*静态内部类
*/
static class InnerClass1{
/* 在静态内部类中可以存在静态成员 */
public static String _name1 = "chenssy_static";

public void display(){
/*
* 静态内部类只能访问外围类的静态成员变量和方法
* 不能访问外围类的非静态成员变量和方法
*/
System.out.println("OutClass name :" + name);
}
}

/**
* 非静态内部类
*/
class InnerClass2{
/* 非静态内部类中不能存在静态成员 */
public String _name2 = "chenssy_inner";
/* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
public void display(){
System.out.println("OuterClass name:" + name);
}
}


public void display(){
/* 外围类访问静态内部类:内部类. */
System.out.println(InnerClass1._name1);
/* 静态内部类 可以直接创建实例不需要依赖于外围类 */
new InnerClass1().display();

/* 非静态内部的创建需要依赖于外围类 */
OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
/* 方位非静态内部类的成员需要使用非静态内部类的实例 */
System.out.println(inner2._name2);
inner2.display();
}

public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.display();
}
}
----------------
Output:
chenssy_static
OutClass name :chenssy
chenssy_inner
OuterClass name:chenssy

匿名内部类

匿名内部类可以简单理解就是没有名字的类。我们直接用例子演示可以说的更清楚。

1
2
3
4
5
6
>button2.addActionListener(  
new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("你按了按钮二");
}
});

相信很多人都写过与上面类似的代码,这其实就是匿名类。其实就是在定义的时候就创建这个类的对象,但因为没有类名,所以以后就不能在创建使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>public class OuterClass {
public InnerClass getInnerClass(final int num,String str2){
return new InnerClass(){
int number = num + 3;
public int getNumber(){
return number;
}
}; /* 注意:分号不能省 */
}

public static void main(String[] args) {
OuterClass out = new OuterClass();
InnerClass inner = out.getInnerClass(2, "chenssy");
System.out.println(inner.getNumber());
}
>}

>interface InnerClass {
int getNumber();
>}

>----------------
>Output:

这里我们就需要看清几个地方

1、 匿名内部类是没有访问修饰符的。

2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

为什么要使用内部类

​ 使用内部类最吸引人的原因是:每个内部类都能独立的继承自一个接口的实现,所以无论外围类是否已经继承了某个接口的实现,对于内部类都没有影响

​ 在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

同时使用内部类还可以获得以下的一些特性:

  1. 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围里对象的信息相互独立。
  2. 在单个外围类中,可以多个内部类以不同的方式实现同一个接口或者继承同一个类。
  3. 创建内部类对象的时刻并不依赖于外部类的创建。
  4. 内部类并没有令人迷惑的”is-a”关系,他就是一个独立的实体。

以上主要摘自《Thinking in java》