可以将一个类的定义放在另一个类的定义内部,这就是内部类. 
内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 
一.内部类的分类 
总的来讲内部类分为普通内部类,匿名内部类,局部内部类,嵌套类(静态内部类)等.下面简要的介绍以下这些内部类定义的语法. 
(1).对于普通的内部类,就是在外围类中插入另一个类的定义.如下面的代码:

 package lkl1;

 ///封装一个包裹类
public class Parcel { ///在一个类的内部定义的另一个类,称之为内部类
///它的定义形式并没有什么不同
public class Destination{
private String label;
Destination(String whereto){
label=whereto;
}
public String getLabel(){
return label;
}
} public class Contents{
private int i=11;
public int value(){
return i;
}
}
///提供一个统一的创建内部类的接口
public Destination destination(String s){
return new Destination(s);
}
public Contents contents(){
return new Contents();
} public void ship(String dest){
//其实也可以直接调用构造器定义
//Contents c1 = new Contents();
Contents c = contents();
Destination d = destination(dest);
System.out.println(d.label);
}
public static void main(String[] args){
Parcel p = new Parcel();
p.ship("changsha"); Parcel p1= new Parcel(); ///为内部类定义引用,注意名字的层次
///在非静态方法中定义内部类的对像需要具体指明这个对象的类型
///OuterClassName.InnerClassName
Parcel.Contents c =p1.contents();
System.out.println(c.i);
Parcel.Destination d = p1.destination("wuhan");
}
}

(2).所谓的匿名内部类的定义语法比较奇特.匿名内部类是没有名字的,所以我们不能想一般的类那样调用构造器得到它的对象,一般我们都将它放在一个方法中,这个方法负责返回这个匿名内部类的一个对象.因为匿名内部类没有名字,所以也就不能通过构造器来实现初始化了,我们可以通过初始化块的形式达到构造器的效果(当然我们是可以调用基类的构造器来初始化基类的成员变量).如下面的代码:

 package lkl1;

 public class Parcel3 {
///用于匿名内部类变量初始化的形参必须要用final修饰
//Destination是前面定义的一个类.
public Destination destination(String dest,final double price,int i){
///创建匿名内部类的一般格式
return new Destination(i){ ////可以通过调用基类的构造器进行基类的初始化
private int cost;
{///用初始化块达到类似于构造器的初始化过程
cost =(int) Math.round(price);
if(cost>100){
System.out.println("Over budget!");
}
}
private String label=dest;
public String readLabel(){
return label;
}
};
}
public static void main(String[] args){
Parcel3 p3= new Parcel3();
Destination d = p3.destination("changsha", 109.234,10);
System.out.println(d.readLabel());
}
}

(3)所谓的局部内部类其实就是在方法和作用域内定义的内部类.这种内部类只在一定的范围是有效的(其实上面的内部类就是一种局部内部类).一般我们是将其当成工具使用的,不希望它是公共可见的.如下面的代码:

 package lkl1;

 public class Test {

           private PrintId create(){
///外部类中的一个方法中定义内部类用于实现某个接口;
///然后返回这个内部类的一个引用
{
///PrintId是前面的定义的一个接口
class Print implements PrintId{
private String id;
public Print(){
id="longkaili";
}
public void print(){
System.out.println(id);
}
}
return new Print();
}
//试图在作用域外访问内部类出错
//(new Print()).id;
}
public static void main(String[] args){
///以下说明了虽然内部类是定义在一个作用域类的
///但是在外面还是可以使用它实现的功能的
Test ts = new Test();
ts.create().print(); ///在本类方法中创建本类的对象,其private接口是可见的
}
}

(4).嵌套类其实就是就将内部类声明成static.对于普通的内部类其必须要依赖于一个外部类的对象,而嵌套类是不需要的,我们可以将其看成一个static型的变量.如下面的代码:

 package lkl1;

 ///当内部类被声明成静态时,我们就将其当成一个静态成员来看待
///此时内部类不在和外部类的对象绑定在一起.
public class Parcel1 { private static class Parcel1Contents extends Contents{
private int i=1;
public int value(){
return i;
}
}
protected static class Parcel1Destination extends Destination{
private String label;
private Parcel1Destination(String whereTo){
label=whereTo;
}
public String readLabel(){
return label;
}
}
//我们可以通过外部类的静态方法创建嵌套类的对象
public static Destination destination(String s){
return new Parcel1Destination(s);
}
public static Contents contents(){
return new Parcel1Contents();
}
public static void main(String[] args){
Contents c= contents();
Destination d =destination("changsha");
}
}

二.外部类和内部类的联系. 
既然内部类定义在了外部类的内部,那么肯定就具有一定的联系的.具体的我们分成非static型的一般内部类和static修饰的嵌套类来分析. 
(1).首先对于一般的内部类,其和外部类的联系是很紧密的.体现在内部类的对象必须依附于一个外部类的对象,也就是说我们必须通过一个外部类的对象才能创建内部类的对象.其次,外部类的一切成员变量对于内部类都是可见的,包括private成员变量,而外部类也可以访问内部类的所有变量.另外,内部类还可以通过.this方式显式的访问其对于的外部类对象,外部类也可以通过.new方式创建内部类的对象(一般我们都是通过外部类的一个方法返回内部类的对象引用).下面的代码示范了这几点:

 package lkl1;

 ///测试外部类对内部类的访问权限
public class Outer {
private int k=100;
private class Inner{
private int i=100;
public int j=1111;
private void print(){
System.out.println("Outer.k = "+ k); ///内部类可以访问外部类的所有成员变量
System.out.println("Inner.print()");
}
///非static内部类中不能创建static类型的变量
/// public static int k=999;
}
public void print(Inner in){
///事实证明外部类同样可以访问内部类的所有方法,变量
///当然前提是我们有一个内部类的对象,直接通过类名来访问是不行的
in.print();
in.i++; in.j++;
System.out.println(in.i);
System.out.println(in.j);
}
public static void main(String[] args){
Outer ot = new Outer();
Outer.Inner in = ot.new Inner(); ///通过.new创建内部类的引用,注意内部类引用的声明方式
ot.print(in);
}
}

(2).对于static修饰的嵌套类来说,情况就不同了.通过上面的例子我们可以看到普通的内部类对象隐式的保存了一个引用,指向创建它的外围对象.但对于嵌套类,它的对象不依赖于外部类的对象而存在,当然它也不能访问非静态的外围类对象.另外还有一点.普通类中是不能包括static方法,变量的,但是嵌套类中是可以包含这些东西的.如下面的代码所示:

 package lkl1;

 ///当内部类被声明成静态时,我们就将其当成一个静态成员来看待
///此时内部类不在和外部类的对象绑定在一起.
public class Parcel1 {
//ContentsheDestination都是前面定义的抽象类
private static class Parcel1Contents extends Contents{
private static int k=110;
private int i=1;
public int value(){
return i;
}
} //我们可以通过外部类的静态方法创建嵌套类的对象
public static Contents contents(){
return new Parcel1Contents();
}
public static void main(String[] args){
///访问Parecel1的静态变量,注意调用格式
System.out.println("Parcel1Contents的静态变量k "+Parcel1.Parcel1Contents.k);
Contents c= contents();
}
}

三.内部类的作用 
内部类的语法是很复杂的,但是在学习这些复杂语法的时候更令我迷惑的是:内部类有什么用?java编程思想一书是这么讲的: 
1.每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响. 
2.可以更好的实现多重继承.我们知道普通的类是不可以多重继承的,但是现在通过内部类,我们就可以对普通类达到多重继承的效果. 
3.在一些设计模式中有重要的应用. 
对于这些现在还不是都能理解的很清楚,希望在以后的学习中能够搞清楚. 
下面给一个通过内部类实现迭代器的实例.下面的Sequence类是一个封装了一个Object数组的普通类,而Selector是一个通用的迭代器接口,我们在Sequence中通过一个内部类实现了Selector接口,然后通过这个内部类的接口向上转型后的对象对Sequence对象进行迭代访问.其实以前我们接触过得集合类的迭代器也就是这么实现的.

 package lkl1;

 ///定义一个通用的迭代器接口
public interface Selector { boolean end();
Object current();
void next();
} package lkl1; ///Sequence类封装一个固定大小的
///Object数组,然后提供它自己的一个迭代器
///这里利用的是内部类可以访问外部类的所有数据的性质
public class Sequence {
private Object[] items;
private int next=0;///保留当前元素个数
public Sequence(int size){
items=new Object[size];
}
public void add(Object obj){
if(next<items.length){
items[next++]=obj;
}
} ///封装一个内部类实现迭代器
private class SequenceSelector implements Selector{
private int i=0; ///当前访问到元素的编号
public boolean end(){
return i==items.length;
}
public Object current(){
return items[i];
}
public void next(){
if(i<items.length) i++;
}
}
///提供迭代器对外的接口
public Selector selector(){
return new SequenceSelector();
} public static void main(String[] args){
Sequence sq= new Sequence(10);
for(int i=0;i<10;i++)
sq.add(Integer.toString(i)); ///利用Sequence本身的迭代器来访问
Selector se =sq.selector();
while(!se.end()){
System.out.print(se.current()+" ");
se.next();
}
System.out.println();
}
}

Java编程思想学习(八) 内部类的更多相关文章

  1. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  2. java编程思想--学习心得

    学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...

  3. Java编程思想学习(一)----对象导论中多态的理解

    1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...

  4. Java编程思想学习录(连载之:内部类)

    内部类基本概念 可将一个类的定义置于另一个类定义的内部 内部类允许将逻辑相关的类组织在一起,并控制位于内部的类的可见性 甚至可将内部类定义于一个方法或者任意作用域内! 当然,内部类 ≠ 组合 内部类拥 ...

  5. Java编程思想学习笔记_3(继承,内部类)

    一.继承与清理 如果某个类需要去清理自身的资源,那么必须用心为其创建回收垃圾的方法,而如果此类有导出的子类,那么必须在导出类中覆盖回收的方法,当覆盖被继承类的回收垃圾的方法的时候,需要注意销毁的顺序应 ...

  6. Java编程思想 学习笔记1

    一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...

  7. Java编程思想学习(五)----第5章:初始化与清理

    随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...

  8. [Java编程思想-学习笔记]第1章 对象导论

    1.1  抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...

  9. Java编程思想学习(十) 正则表达式

    正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...

随机推荐

  1. 【转】UVALive 5964 LCM Extreme --欧拉函数

    题目大意:求lcm(1,2)+lcm(1,3)+lcm(2,3)+....+lcm(1,n)+....+lcm(n-2,n)+lcm(n-1,n)解法:设sum(n)为sum(lcm(i,j))(1& ...

  2. HDU 2899 Strange fuction 【三分】

    三分可以用来求单峰函数的极值. 首先对一个函数要使用三分时,必须确保该函数在范围内是单峰的. 又因为凸函数必定是单峰的. 证明一个函数是凸函数的方法: 所以就变成证明该函数的一阶导数是否单调递增,或者 ...

  3. 详解Java的MyBatis框架中SQL语句映射部分的编写

    这篇文章主要介绍了Java的MyBatis框架中SQL语句映射部分的编写,文中分为resultMap和增删查改实现两个部分来讲解,需要的朋友可以参考下 1.resultMap SQL 映射XML 文件 ...

  4. AC日记——搞笑世界杯 codevs 1060(dp)

    题目描述 Description 随着世界杯小组赛的结束,法国,阿根廷等世界强队都纷纷被淘汰,让人心痛不已. 于是有 人组织了一场搞笑世界杯,将这些被淘汰的强队重新组织起来和世界杯一同比赛.你和你的朋 ...

  5. AC日记——阶乘之和 洛谷 P1009(高精度)

    题目描述 用高精度计算出S=1!+2!+3!+…+n!(n≤50) 其中“!”表示阶乘,例如:5!=5*4*3*2*1. 输入输出格式 输入格式: 一个正整数N. 输出格式: 一个正整数S,表示计算结 ...

  6. JavaScript测试工具

    大家都知道Javascript的测试比较麻烦,一般是开发使用一些浏览器的插件比如IE develop bar或是firebug来调试,而测试往往需要通过页面展示后的js错误提示来定位.那么还有其他比较 ...

  7. Hibernate Java、Hibernate、SQL 之间数据类型转换

    Hibernate映射类型 Java类型 标准SQL类型  integer  java.lang.Integer  integer  long  java.lang.Long  bigint  sho ...

  8. c#中结构体(struct)和类(class)的区别

    一.类与结构的示例比较: 结构示例: public struct Person { string Name; int height; int weight public bool overWeight ...

  9. python playfair

    #########################Playfair密码######################### #约定1:若明文字母数量为奇数,在明文末尾添加一个'Z' #约定2:'I'作为 ...

  10. 承香墨影 Android--Matrix图片变换处理

    承香墨影 Android--Matrix图片变换处理 前言 本篇博客主要讲解一下如何处理对一个Bitmap对象进行处理,包括:缩放.旋转.位移.倾斜等.在最后将以一个简单的Demo来演示图片特效的变换 ...