言成言成啊 | Kit Chen's Blog

内部类及其相关知识点

2019-10-08

1. 内部类的概念与分类

如果一个事物的内部包含了另一个事物,那么就是一个类内部包含了另外一个类。

例如:身体和心脏的关系,汽车和发动机的关系。

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

2. 成员内部类的定义及其使用

成员内部类的定义格式:

1
2
3
4
5
6
修饰符 class 类名称 {
修饰符 class 内部类名称 {
//..
}
//..
}

如何使用成员内部类?两种方式:

  1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法

  2. 直接方式:记住公式

    1
    2
    类名称 对象名 = new 类名称();//一般的对象创建方式
    外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

注意:

内用外,随意访问;

外用内,一定需要借助内部类对象

Body.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Body {//外部类
public void methodBody() {
System.out.println("外部类的方法");
new Heart().beat();
}
private String name;

public Body() {
this.name="娃哈哈";
}

public Body(String name) {
this.name=name;
}
public class Heart{//内部类,而且是成员内部类
//内部类方法
public void beat() {
System.out.println("心脏跳动:嘭嘭嘭");
System.out.println("我叫:"+name);//娃哈哈
}
}
}

Main.java

1
2
3
4
5
6
public static void main(String[] args) {
new Body().methodBody();//间接方式访问

//直接方式访问
new Body().new Heart().beat();
}

如果外部类跟内部类的成员变量重复了咋整?

1
2
3
4
5
6
7
8
9
10
11
12
public class Outer {
int num=10;//外部类的成员变量
public class Inner /*extends Object*/ {//这个地方表示所有的东西其实都是继承自Object
int num=20;//内部类的成员变量
public void methodInner(){
int num=30;//内部类的局部变量
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(Outer.this.num);//30
}
}
}

如果重现重名现象

1
外部类名称.this.外部类成员变量名//访问外部类里面的变量

Main.java

1
2
3
4
5
public static void main(String[] args){
new Outer().new Inner().methodInner();
//上下这两个其实是等价的
Outer.Inner inner = new Outer().new Inner();
}

3. 局部内部类的定义及其使用

如果一个类是定义在一个方法内部的,那么这就是一个局部内部类

局部:只有当前所属的方法才能使用他,出了这个方法外面就不能用了

定义格式:

1
2
3
4
5
6
7
修饰符 class 外部类名称 {
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称 {
//....
}
}
}

类的权限修饰符:

public>protected>(default)>private

定义一个类,权限修饰符规则:

  1. 外部类:只能用public/(default)
  2. 成员内部类:public/protected/(default)/private
  3. 局部内部类:什么都不写

Outer01.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Outer01 {
public void methodOuter() {
class Inner {//局部内部类
int num=10;
public void innerMethod() {
System.out.println(num);//10
}
}

//局部内部类,只有在这个范围内才能使用,所以,看下面
new Inner().innerMethod();
}
}

Main.java

1
2
3
public static void main(String[] args) {
new Outer01().methodOuter();
}

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是有效final

有效final:即使没写final,如果只赋值一次,也是有效final。写上final会更保险,更好

备注:从Java 8开始,只要局部变量不变,那么final就可以省略。

原因:

  1. new出来的对象在堆内存中
  2. 局部变量是跟着方法走的,在栈内存中
  3. 方法运行结束之后,立刻出栈,局部变量就会消失
  4. new出来的对象会在堆中持续存在,直至垃圾回收消失

Outer02.java

1
2
3
4
5
6
7
8
9
10
11
12
public class Outer02 {
public void methodOuter() {
int num=10;//所在方法的局部变量
//num=20;//如果再加上这一句,就会报错了,因为必须是有效final
class Inner {
public void methodInner() {
System.out.println(num);
}
}
new Inner().methodInner();
}
}

Main.java

1
2
3
public static void main(String[] args) {
new Outer02().methodOuter();
}

4. 匿名内部类(属于局部内部类)的定义及其使用

如果接口的实现类,或者是父类的子类,只需要使用唯一的一次

那么这种情况下,就可以省略该类的定义,而使用匿名内部类。

匿名内部类的定义格式:

1
2
3
接口名称 对象名 = new 接口名称(){
//覆盖重写接口中所有的抽象方法
}

new 接口名称()表示要继承的接口,后面的{}才是匿名内部类

对格式“new 接口名称(){…}”进行解析:

  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类要实现的接口
  3. {…}才是匿名内部类的内容

注意:

  1. 匿名内部类,在创建对象的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了
  2. 匿名对象,在调用方法的时候,只能调用唯一一次
    如果希望同一个对象,调用多次方法,那么必须给对象起名
  3. 匿名内部类是省略了实现类或者子类,匿名对象是省略了对象名称
    强调:匿名内部类和匿名对象不是一回事
    匿名内部类有对象名称,匿名对象没有

MyInterface.java

1
2
3
4
5
public interface MyInterface {
void methodAbs();//抽象方法,隐藏了public abstract

void method();
}

MyInterfaceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyInterfaceImpl implements MyInterface {

@Override
public void methodAbs() {
System.out.println("实现类覆盖重写了方法");
}

@Override
public void method() {
System.out.println("实现类覆盖重写了方法");
}

}

Main.java

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
public static void main(String[] args) {
//MyInterfaceImpl impl=new MyInterfaceImpl();
//MyInterface impl=new MyInterfaceImpl();//多态写法
//impl.methodAbs();


//可以使用匿名内部类写
MyInterface impl=new MyInterface() {
@Override
public void methodAbs() {
System.out.println("匿名内部类覆盖重写了方法");
}

@Override
public void method() {
System.out.println("匿名内部类覆盖重写了方法");
}
};

impl.methodAbs();
impl.method();

System.out.println("===============");

//使用了匿名内部类,并且省略了对象名称,也就是匿名对象
new MyInterface() {

@Override
public void methodAbs() {
System.out.println("匿名内部类覆盖重写了方法");
}

@Override
public void method() {
System.out.println("匿名内部类覆盖重写了方法");
}
}.methodAbs();
//因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
new MyInterface() {
@Override
public void methodAbs() {
System.out.println("匿名内部类覆盖重写了方法");
}

@Override
public void method() {
System.out.println("匿名内部类覆盖重写了方法");
}
}.method();
}

5. 类以及接口作为成员变量

Skill.java

1
2
3
public interface Skill {
void useSkill();
}

SkillImpl.java

1
2
3
4
5
6
7
8
public class SkillImpl implements Skill {

@Override
public void useSkill() {
System.out.println("释放技能:国士无双");
}

}

Weapon.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Weapon {
private String code;// 武器代号

public Weapon() {

}

public Weapon(String code) {
this.code = code;
}

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}
}

Hero.java

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 Hero {
private String name;// 英雄名字
private int age;// 英雄的年龄
private Weapon weapon;// 武器
private Skill skill;//技能

public Hero() {

}

public Hero(String name, int age, Weapon weapon,Skill skill) {
this.name = name;
this.age = age;
this.weapon = weapon;
this.skill=skill;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Weapon getWeapon() {
return weapon;
}

public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}

public Skill getSkill() {
return skill;
}

public void setSkill(Skill skill) {
this.skill = skill;
}

public void attack() {
System.out.println("英雄");
System.out.println("年龄:"+this.getAge()+" 名称:"+this.getName()+" 武器:"+this.getWeapon().getCode());
this.getSkill().useSkill();
System.out.println("Attack enemy!");
}

}

Main.java

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 Demo06Main {
public static void main(String[] args) {
//创建一个英雄角色
Hero hero =new Hero();

//为英雄起名、设置年龄
hero.setName("韩信");
hero.setAge(20);

//创建一个Weapon对象
Weapon weapon =new Weapon();
weapon.setCode("枪");

//为英雄配备武器
hero.setWeapon(weapon);

//设置技能:1.可以使用单独定义的实现类 2.可以使用匿名内部类
hero.setSkill(new SkillImpl());//使用单独定义的实现类

//还可以使用匿名内部类(下面使用的是匿名内部类和匿名对象)
hero.setSkill(new Skill() {
@Override
public void useSkill() {
System.out.println("释放技能:背水一战");
}
});

//英雄Attack
hero.attack();
}
}
阅读量