Java内部类杂谈

这两天读了《Thinking in Java》中的内部类这一节,结合从网上查阅资料,感觉自己对内部类的认识加深了很多,所以写一篇杂(shui)谈(wen).

在很早的时候,说起内部类,我对于它的理解就是在一个类中又定义了一个类,这个类可以访问外边类的成员变量,但是外边成员不能访问内部类的成员变量.可以通过一个外部类的实例去创建一个内部类的实例.

在读书之后,我想Java的设计团队在创建内部类这个机制时想的比我之前理解的要复杂的多.

首先,为什么要存在内部类?

我们都知道Java与C++有一点很大的不同就是C++可以继承多个类,但是Java只能继承一个类,然后再通过实现接口的方式拓展自己的功能.但是偏偏有一个出现了一个场景要求我们用一个类去继承两个基类.这个时候内部类就可以很好的解决这个问题,我们可以创建一个外部类继承其中一个类,然后在外部类中创建一个内部类去继承另一个类,这样就解决了这个问题.而且在继承这一块上内部类还可以提供更多的便利,比如我们想对一个基类有多个截然不同的实现方式,然后再有一个统一的工具类去管理他们,这就可以用内部类去做啊,我们创建一个外部类作为管理器,然后在这个外部类中定义多个继承同一个类的内部类,然后让这些内部类有属于自己的实现方式即可,然后在外部类里写几个工厂方法或者其他功能方法去操纵这些内部类.

还不仅仅是这样,内部类的出现,可以让我们在创建一个类时让这个类的内部结构更加条理清晰.

//比如我们要创建一个Car类
public class Car {
    
    private double Size;
    private String color;
    private String owner
    
    private String wheelsColor;
    private String wheelsSzie;
        
    private String doorColor;
    private String doorSize;
}

这个Car类中包含了许许多多的信息,但是一股脑的放到一到一起是不是有点乱啊,而且像车轮这样的东西一个车子并不只有一个啊,万一我想前后车轮的颜色不一样呢,在定义几个轮子吗?当然不是了.这时候,我们可以使用内部类把外部类的属性分离一下.

public class Car {
    
    private double Size;
    private String color;
    private String owner
    
    class Wheel {
        private String wheelsColor;
        private String wheelsSzie;
    }
    
    class Door {
        private String doorColor;
        private String doorSize;
    }    
   
}

这样一看就清晰许多了,而且由于内部类的机制(只能通过一个外部类的实例对象去创建内部类实例)我们不用担心轮子实例被随意创建出来,从而闹出没有车却有好多个车轮这样的笑话.

说到内部类的创建,我想Java语言的设计者对于内部类这个功能的展望应该就是想让它作为外部类的一个个小部件吧.没有外部类就没有内部类(除了静态内部类),而且在普通的内部类中也不允许有然后static关键字.我之前对这个也不太理解,现在在我看来这就是必须的必,之前说过,普通内部类是作为外部类的小组件的身份存在的,所以在外部类的实例对象创建之前,内部类是不能有任何信息出现的,但是如果内部类存在static修饰的方法或者成员变量就不行了,static变量的创建实在类加载的时候,这个时候外部类的实例不能保证存在,所以这时候内部类的部分就被创建出来了这种事和之前的思路简直是背道而行的嘛,所以设计者禁用了普通内部类中static的使用.一句话来说普通内部类对象的诞生必要要有一个外部类,怎么体现这一点呢?可以再创建一个类,继承自一个内部类,然后创建一个新类的实例,这时候会对它的父类也进行初始化,这个类的父类是一个内部类,内部类的实例化需要一个相应的外部类,所以你还得传一个这个内部类的父类的引用到构造函数才能使这个新类的实例创建成功.

内部类是可以访问外部类的所有成员变量的,private成员变量也可以,这是因为在内部类中有一个指向外部类的引用.上面那个例子传到构造函数中的外部类其实就是要赋值给这个引用的.但是外部类却无法访问内部类的成员变量,这还是因为内部类的设计初衷,内部类是作为外部类的一个组件诞生的,所以在创建内部类的时候是要一个外部类的对象的的,也就是在内部类创建的时候肯定已经存在了一个外部类的对象,所以在内部类中访问所有外部类的成员变量都没有什么问题.但是反过来就不可以了,在外部类中用内部类的成员变量就不行了,因为在创建外部类实例对象后并不能保证有一个内部类对象存在,所以这时调用内部类中的成员变量会有矛盾.

静态内部类,就是在定义内部类的地方加上static,因为这一个关键字,使得内部类的地位发生了很大的变化,如果外部类和普通内部类的关系是爸爸和儿子的关系(有爸爸才能有儿子).那么外部类和静态内部类的关系就像一个人和他的身份证这样的关系,静态内部类的创建不需要外部类实例对象,但是这也就不能保证在内部类实例创建时有一个外部类实例存在这个情况,所以在静态内部类中只能使用外部类的static变量和方法.

匿名内部类,这多用于想创建一个仅仅实现某个接口(继承某个类)的类,直接在new 关键字后面写这个接口(基类)的名称即可,然后在后边的花括号里边编写你自己的实现.

//一个盘子类,可以调用方法获得各种水果
public class Bowl {
    
    public Fruit getApple(){
        //返回一个实现了水果接口的类
        return new Fruit() {
            //实现接口的方法
            public String getName() {
                return "Apple";
            }
     
            public int getQuantity() {
                return 10;
            } 
        };
    }

    .....
}

//一个名为水果的接口,所有的水果类必须实现它
interface Fruit {
    
    String getName();
    int getQuantity();
}

Java虚拟机读到这段代码时.会为你创建一个随机名字的类去实现接口(继承类).接口具体的实现方法就是后边花括号里边写的那样.这就是匿名内部类.它允许程序员去拓展一个存在类或接口.如果是拓展类,则只能去覆盖这个类存在的方法,实现接口则是只能重新接口中指定的方法,在匿名内部类中新添加的方法是没有意义的,匿名内部类本来就是为了让我们可以简化书写和覆盖方法而存在的,由于它没有名字,所以我们无法创建它的引用,自然也无法调用它特有的方法.只能是把内部类向上转型,然后通过多态机制去使用它.

最后来说说内部类使用局部变量的问题,内部类中使用的局部变量需要加上final关键字修饰.为什么呢?

//一个盘子类,可以调用方法获得各种水果
public class Bowl {
    
    public Fruit getDiyFruit(final String name, final int quantity){
        //返回一个实现了水果接口的类
        return new Fruit() {
            //实现接口的方法
            public String getName() {
                return name;
            }
     
            public int getQuantity() {
                return quantity;
            } 
        };
    }

    .....
}

//一个名为水果的接口,所有的水果类必须实现它
interface Fruit {
    
    String getName();
    int getQuantity();
}

先来说一下java的传参方式,java的变量分为两种,基本类型和对象类型,在传参是基本类型是直接传值的,传对象则是把指向对象的这个引用传进去了.用final修饰变量后,基本类型参数无法更改值,对象的引用无法指向其他的地址.在内部类使用局部变量时,这些局部变量都是被java虚拟机做了一份拷贝后然后把拷贝放到内部类的构造函数里的(可以反编译一下看看),相当于内部类也有了一份属于自己的变量.为什么呢?因为我们定义的局部变量基本都是朝生暮死的,他们随着方法进入栈,随着方法的执行结束出栈,出栈后就被销毁了.但内部类不一样啊,就想上面的代码那样,内部类被作为返回值送到其他地方了,方法出栈时内部类还在使用呢,这是要是内部类依赖的局部变量被销毁了那就会发生大问题了,所以根据方法体本身和内部类的生命周期不同,在给构造函数传参使用局部变量时传参方法与java通常的方法不同.好吧,既然是做了一份拷贝传进去了,那么这份拷贝的数据做了任何的修改会对原数据有什么影响呢?答案是丝毫没影响,这就造成了变量值存在差异的矛盾了(明明看着是同一个变量,怎么能拥有不同的值呢),但是java设计者目前还没有做让内外变量的值统一的这个机制,于是干脆就规定在内部类内部和方法体中公用的变量都要用final修饰,这样一来,里外都无法修改变量了,也就让变量的值一致了.

Java真的是一门特别棒的语言,它既设计严谨,使用方便还有高效率,这可能就是它能存活至今的原因吧.本文到这里就结束了,这是笔者自己对内部类部分的理解,如果有错的地方还请大家指出.内部类只一块远不止这些,今后也要继续学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值