编程语言
首页 > 编程语言> > 夯实Java基础(十一)——内部类

夯实Java基础(十一)——内部类

作者:互联网

1、内部类的概念

内部类顾名思义:将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。对于很多Java初学者来说,内部类学起来真的是一头雾水,根本理解不清楚是个什么东西,包括我自己(我太菜了!哈哈),所以接下来我要好好地来研究一下。

我们来看下内部类的定义格式;

public class OuterClass {
        //code
    class InnerClass{
        //code
    }
}

这里的InnerClass就是一个内部类。无论在我们的学习中还是工作中,内部类用到的地方真的不是很多,一般都出现在源码中,但是我们还是要搞懂内部类,因为后面对我们阅读源码非常有帮助。而且随着后面我们编程能力的提高,自然而然会领悟到它的魅力所在,它能够让我们设计出更加优雅的程序结构。在使用内部类之前我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处。

在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

也就是说内部类拥有类的基本特征(可以继承父类,实现接口)。在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。(注:内部类可以嵌套内部类,但是这极大的破换了代码的结构,这里不推荐使用)

那我们来看一下使用内部类进行多继承,接口多继承就不举例了,因为接口本身就可以实现多继承。

 1 class Father{
 2     public String handsome(){
 3         return "爸爸很帅气";
 4     }
 5 }
 6 
 7 class Mother{
 8     public String beautiful(){
 9         return "妈妈很漂亮";
10     }
11 }
12 
13 class Son{
14     //内部类继承了Father类
15     class MyFather extends Father{
16         //重写父类方法
17         public String handsome(){
18             return "我遗传了爸爸的帅气";
19         }
20     }
21     //内部类继承了Mother类
22     class MyMother extends Mother{
23         //重写父类方法
24         public String beautiful(){
25             return "我遗传了妈妈的漂亮";
26         }
27     }
28 }
29 
30 public class Test {
31     public static void main(String[] args) {
32         Son son=new Son();
33         Son.MyFather myFather=son.new MyFather();
34         System.out.println(myFather.handsome());
35         Son.MyMother myMother=son.new MyMother();
36         System.out.println(myMother.beautiful());
37     }
38 }

 运行结果:

从上面的举例代码可以看出,两个内部类分别继承了Father、Mother类,并且重写了父类的方法,这是内部类最重要的特性:内部类可以继承一个与外部类无关的类,保证了内部类的独立性,正是基于这一点,多重继承才会成为可能。

可以发现在创建内部类实例的时候,使用了 .new 这个特征,与以往我们创建实例不太相同。.new可以这样理解:根据外部类来创建内部类的对象实例。

Java中内部类可分为四种:成员内部类、局部内部类、匿名内部类、静态内部类。下面我们逐一介绍这四种内部类:

2、成员内部类

成员内部类是定义在类中的类。我们可以把成员内部类看成是外部类的一个成员,所以成员内部类可以无条件访问外部类的所有成员属性和成员方法,包括private成员和静态成员。但是外部类要访问内部类的成员属性和方法则需要通过内部类实例来访问。当成员内部类拥有和外部类同名的成员变量或者方法时,会优先访问的是成员内部类的成员,但是我们可以使用 .this(如果有继承可以使用super)来访问外部类的变量和方法。

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

  1. 成员内部类中不能存在任何static的变量和方法;
  2. 成员内部类是依附于外部类的,所以只有先创建了外围类才能够创建内部类(静态内部类除外)。
 1 class OuterClass{
 2     private String outerName="tang_hao_outer";
 3     private int outerAge=22;
 4 
 5     public OuterClass() {
 6     }
 7 
 8     //成员方法
 9     public void outerMethod() {
10         System.out.println("我是外部类的outerMethod方法");
11     }
12 
13     //外部类静态方法
14     public static void outerStaticMethod() {
15         System.out.println("我是外部类的outerStaticMethod静态方法");
16     }
17     //定义返回内部类实例的方法,推荐使用该方法来换取内部类实例
18     public InnerClass getInnerClassInstance(){
19         return new InnerClass();
20     }
21 
22     //内部类
23     class InnerClass{
24         private String innerName="tang_hao_Inner";
25         private int innerAge=21;
26 
27         public InnerClass() {
28         }
29 
30         public void show(){
31             //当名字和外部类一样时,默认调用内部类的成员属性
32             System.out.println("内部类变量:"+innerName);
33             System.out.println("内部类变量:"+innerAge);
34             //当名字和外部类一样时,可以使用 。this来调用外部类属性
35             System.out.println("外部类变量:"+OuterClass.this.outerName);
36             System.out.println("外部类变量:"+OuterClass.this.outerAge);
37             //访问外部类的方法
38             outerMethod();
39             outerStaticMethod();
40         }
41     }
42 }
43 public class Test {
44     public static void main(String[] args) {
45         //普通方法创建实例
46         OuterClass outerClass=new OuterClass();
47         OuterClass.InnerClass innerClass=outerClass.new InnerClass();
48         innerClass.show();
49         System.out.println("-------------------");
50         //调用外部类的getInnerClassInstance来创建内部类实例
51         OuterClass.InnerClass innerClassInstance = outerClass.getInnerClassInstance();
52         innerClassInstance.show();
53     }
54 }

 运行结果:

从上面示例中,当内部类和外部类的变量和方法一样时,我们用了 .this来调用外部类的属性(静态除外,因为静态随类加载而加载,优于对象的创建),它可以理解为:产生一个指向外部类的引用。还有如果该内部类的构造函数无参数,强烈推荐使用类似getInnerClassInstance()这样的方法来获取成员内部类的实例对象。

3、局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。注意:局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。

局部内部类一般都用于返回一个类或实现接口的实例。我们用Comparable接口为例:

 1 class OuterClass{
 2     //创建返回一Comparable接口实例的方法
 3     public Comparable getComparable(){
 4         //创建一个实现Comparable接口的内部类:局部内部类
 5         class MyComparable implements Comparable{
 6             @Override
 7             public int compareTo(Object o) {
 8                 return 0;
 9             }
10         }
11         //返回实现Comparable接口的实例
12         return new MyComparable();
13     }
14 }

当我们创建外部类的实例调用getComparable()方法时,就可以轻松获取实现Comparable接口的实例了。

注意:局部内部类如果想用方法传入形参,该形参必须使用final声明(JDK8形参变为隐式final声明)。上面的例子如果是getComparable(Object o),那么这个形参前面就隐式加了final关键字。

4、匿名内部类

匿名内部类就是没有名字的内部类。

 1 //创建一个接口
 2 interface IPerson{
 3     public void eat();
 4     public void sleep();
 5 }
 6 
 7 public class OuterClass {
 8     //这里注意,局部内部类如果需要通过方法传入参数,该形参必须使用final声明(JDK8形参变为隐式final声明)
 9     //我用的JDK8,所以这里没有显式的加final,但是JVM会自动加
10     public static IPerson getInnerClassInstance(String eat,String sleep){
11         return new IPerson() {
12             @Override
13             public void eat() {
14                 System.out.println(eat);
15             }
16 
17             @Override
18             public void sleep() {
19                 System.out.println(sleep);
20             }
21         };//这个分好要注意
22     }
23 
24     public static void main(String[] args) {
25         IPerson person = OuterClass.getInnerClassInstance("吃饭", "睡觉");
26         person.eat();
27         person.sleep();
28     }
29 }

 

运行结果:吃饭、睡觉

我们知道在抽象类和接口中是不能被实例化的,但是在匿名内部类中我们却看见new了一个IPerson接口,这是怎么回事。这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。

 

匿名内部类其实就是一个没有名字的方法内部类,所以它符合方法内部类的所有约束,初次之外,还有一些地方需要注意:

匿名内部类是没有访问修饰符的。
匿名内部类必须继承一个抽象类或者实现一个接口
匿名内部类中不能存在任何静态成员或方法
匿名内部类是没有构造方法的,因为它没有类名。
一般使用匿名内部类的场景是,要继承或实现的接口只有一个抽象方法,比如添加一个监听器:

5、静态内部类

静态内部类是指用static修饰的内部类。在前面夯实Java基础(七)——Static关键字中提到了static关键字可以修饰内部类。我们知道普通类是不允许声明为静态的,只要内部类才可以,被static修饰的内部类它不依赖于外部类的实例。这是因为非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类。

static修饰内部类注意几点:

简单举例:

 1 class OuterClass{
 2     //静态变量
 3     private static int static_num=66;
 4     //非静态变量
 5     private int num=99;
 6 
 7     //静态内部类
 8     static class InnerStaticClass{
 9         public void print(){
10             //静态内部类只能访问外部类的静态变量和静态方法
11             System.out.println("静态内部类方法print()=="+static_num);
12             staticShow();
13         }
14     }
15     //非静态内部类
16     class InnerClass{
17         public void display(){
18             //非静态内部类中可以调用外部类的任何成员,不管是静态的还是非静态的
19             System.out.println("外部类静态变量=="+static_num);
20             System.out.println("外部类普通变量=="+num);
21             show();
22             System.out.println("非静态内部类方法display()=="+num);
23 
24         }
25     }
26     public void show(){
27         System.out.println("外部类非静态show()方法");
28     }
29     public static void staticShow(){
30         System.out.println("外部类静态staticShow()方法");
31     }
32 }
33 public class Test {
34     public static void main(String[] args) {
35         //static对象实例
36         OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass();
37         staticClass.print();
38 
39         //非static对象实例
40         OuterClass outerClass=new OuterClass();
41         OuterClass.InnerClass innerClass=outerClass.new InnerClass();
42         innerClass.display();
43     }
44 }

运行结果:

从上面的例子我们可以看到静态内部类和非静态内部类的区别。

参考文章:https://www.cnblogs.com/chenssy/p/3388487.html

标签:部类,Java,内部,十一,静态,OuterClass,夯实,static,public
来源: https://www.cnblogs.com/tang-hao-/p/11255923.html