0%

浅谈Java内部类

前言

本篇博客主要涉及以下几个内容

  1. Java中的内部类
  2. Java中的静态内部类
  3. 关于内部类常见的一些面试题

Java中的内部类

内部类就是在Java类内部定义的类,主要分为四种类型:

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类

使用内部类的原因

  1. Java拥有单继承的特性,而内部类可以独立继承一个类或实现接口。这一定程度上方便了开发。
  2. 内部类的信息与其他外围类信息独立,提供了更好的封装

成员内部类

作为外部类的成员变量存在,其性质如下:

  1. 可以有访问修饰符
  2. 外部类在类中访问内部类需要通过new的方式
  3. 外部类加载,成员内部类不加载
  4. 不能有静态信息【因为如果外部类没有实例化,则内部类不会被加载(不符合类加载的几种条件),因此静态变量不会进内存,这与static的概念矛盾了】
  5. 内部类的this关键字表示自己的对象,如要访问外部类的对象则使用外部类名.this
  6. 在外部类外访问内部类需要使用外部类的实例引用.new 内部类构造函数
  • 以下demo体现了这些特性:
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
package memoforward;

public class TestOuterInnerClass{
public static void main(String[] args) {
//访问内部类
//1. 先要有外部类的应引用
OuterInnerClass outer = new OuterInnerClass();
//2. 通过该引用创建外部类
OuterInnerClass.InnerClass inner = outer.new InnerClass();
//3. 也可以通过自定义的方法创建
//inner = outer.creatInner();
//4.直接使用内部类
inner.print();
/*
输出结果如下:
1. Outer Constructor..
2. Inner Constructor
3. Outer's val:10
4. Inner's val:5
5. outerPrint..
*/
}
}

class OuterInnerClass /*extends A implements IA*/{
private int val = 10;
public OuterInnerClass() {
System.out.println("Outer Constructor..");
}
private void outerPrint(){
System.out.println("outerPrint..");
}
public class InnerClass /*extends B implements IB*/{
//内部类变量有重名
private int val = 5;
public InnerClass(){
System.out.println("Inner Constructor");
}
public void print(){
//外部类和内部类的成员变量重名如何区分?
System.out.println("Outer's val:" + OuterInnerClass.this.val);
System.out.println("Inner's val:" + this.val);
//可以直接访问外部类的成员变量和方法
outerPrint();
}
}
public InnerClass creatInner() {
// 外部类中访问内部类通过new的方式
return new InnerClass();
}
}

静态内部类

静态内部类的性质如下:

  1. 可以有访问修饰符
  2. 静态内部类不依赖于外部类。因此外部类被加载,内部类不会被加载;而内部类被加载,外部类也不会被加载
    1. 内部类创建:Outer.Inner inner = new Outer.Inner()
    2. 访问内部类的静态成员:Outer.Inner.staticValue
  3. 静态内部类只可以关联外部类的静态资源(因为内部类被加载时,关联外部类的静态资源会使外部类加载,但是不能拥有外部类的实例)
  4. 与成员内部类的区别
    1. 不需要依赖于外部类的实例创建
    2. 只能关联外部类静态资源
    3. 不存在this关键字
  • 以下demo可以体现以上性质:
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
package memoforward.test;

public class TestStaticOuterInner {
public static void main(String[] args) {
//生成静态内部类的实例
Outer.Inner inner = new Outer.Inner();
//类外访问静态内部类的静态资源
Outer.Inner.innerStaticMethod();
//调用内部类的方法
inner.innerMethod();
/*
输出为:
1. inner static block...
2. Inner constructor...
3. innerStaticMethod...
4. Outer static block...
5. Outer static value:10
6. innerMethod...
*/
}
}

class Outer{
static{
System.out.println("Outer static block...");
}
private static int a = 10;

public Outer(){
System.out.println("Outer constructor...");
}
static class Inner{
static{
System.out.println("inner static block...");
}
public static void innerStaticMethod(){
System.out.println("innerStaticMethod...");
//可以访问外部类的静态变量
System.out.println("Outer static value:" + a);
}

public Inner(){
System.out.println("Inner constructor...");
}
public void innerMethod(){
System.out.println("innerMethod...");
}
}

public static void outStaticMethod(){
//类中访问静态内部类
Inner inner = new Inner();
inner.innerMethod();
}
}
  • 输出解释:
  1. 首先创建静态内部类的实例,先加载内部类执行静态代码块,随后执行构造方法。由静态内部类的性质可知,加载静态内部类,外部类是不加载的。
  2. 随后执行静态内部类的静态方法,此方法关联了外部类的静态变量,因此外部类被加载,但没有实例化。
  3. 最后执行了静态内部类实例的方法。可见在整个流程中,外部类只是加载了,并没有实例化。

局部内部类

在一个方法内定义的类,有如下性质:

  1. 没有访问权限修饰符
  2. 仅在方法内有效
  3. 无法创造静态信息,因为这个类是临时的
  4. 可以访问外部类的所有信息(方法内部本身就是可以访问的)
  5. 可以访问方法的参数和局部变量(但是这些变量始终不能被改变)

匿名内部类

常用来重写某个或某些方法,这个用的还是比较多的,比较常见的就是Java的动态代理实现,会实现一个匿名内部类并重写invoke()方法。

  • 使用动态代理的Demo
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
package memoforward.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestAnonInnerClass {
public static void main(String[] args) {
IPerson person = PersonProxy.getPerson();
person.eat();
}
/*
输出结果:
1. 做饭...
2. 吃饭...
3. 洗碗...
*/
}

interface IPerson{
void eat();
}
class Person implements IPerson{
@Override
public void eat() {
System.out.println("吃饭...");
}
}

class PersonProxy{
static IPerson person = new Person();
public static IPerson getPerson(){
//使用匿名内部类InvocationHandler,重写invoke方法,对Person接口的所有方法进行增强
return (IPerson) Proxy.newProxyInstance(person.getClass().getClassLoader()
, person.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
try{
System.out.println("做饭...");
obj = method.invoke(person);
return obj;
}catch(Exception e){
System.out.println("饭馊了...");
throw new RuntimeException("没吃上饭:" + e);
}finally{
System.out.println("洗碗...");
}
}
});
}
}