摘要

内部类?成员内部类?局部内部类?匿名内部类?类以及接口作为成员变量?

正文

1. 内部类的概念与分类

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

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

分类:

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

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

成员内部类的定义格式:

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

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

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

  2. 直接方式:记住公式

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

注意:

内用外,随意访问;

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

Body.java

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

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

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

java
 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
        }
    }
}

如果重现重名现象

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

Main.java

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. 局部内部类的定义及其使用

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

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

定义格式:

注释
修饰符 class 外部类名称 {
 修饰符 返回值类型 外部类方法名称(参数列表){
  class 局部内部类名称 {
   //....
  }
 }
}

类的权限修饰符:

public>protected>(default)>private

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

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

Outer01.java

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

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

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

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

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

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

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

匿名内部类的定义格式:

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

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

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

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

注意:

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

MyInterface.java

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

MyInterfaceImpl.java

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

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

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

SkillImpl.java

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

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

}

Weapon.java

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

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

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();
	}
}