可以将一个类的定义放在另一个类的定义内部,这就是内部类.
内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结.
一.内部类的分类
总的来讲内部类分为普通内部类,匿名内部类,局部内部类,嵌套类(静态内部类)等.下面简要的介绍以下这些内部类定义的语法.
(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编程思想学习(八) 内部类的更多相关文章
-
[Java编程思想-学习笔记]第3章 操作符
3.1 更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...
-
java编程思想--学习心得
学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...
-
Java编程思想学习(一)----对象导论中多态的理解
1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...
-
Java编程思想学习录(连载之:内部类)
内部类基本概念 可将一个类的定义置于另一个类定义的内部 内部类允许将逻辑相关的类组织在一起,并控制位于内部的类的可见性 甚至可将内部类定义于一个方法或者任意作用域内! 当然,内部类 ≠ 组合 内部类拥 ...
-
Java编程思想学习笔记_3(继承,内部类)
一.继承与清理 如果某个类需要去清理自身的资源,那么必须用心为其创建回收垃圾的方法,而如果此类有导出的子类,那么必须在导出类中覆盖回收的方法,当覆盖被继承类的回收垃圾的方法的时候,需要注意销毁的顺序应 ...
-
Java编程思想 学习笔记1
一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...
-
Java编程思想学习(五)----第5章:初始化与清理
随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...
-
[Java编程思想-学习笔记]第1章 对象导论
1.1 抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...
-
Java编程思想学习(十) 正则表达式
正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...
随机推荐
-
设计模式之创建类模式大PK
创建类模式大PK 创建类模式包括工厂方法模式.建造者模式.抽象工厂模式.单例模式和原型模式,他们能够提供对象的创建和管理职责.其 ...
-
Kinect2在线重建(Tracking and Mapping)
前言 个人理解错误的地方还请不吝赐教,转载请标明出处,内容如有改动更新,请看原博:http://www.cnblogs.com/hitcm/ 如有任何问题,feel free to ...
-
Troubleshooting &#39;library cache: mutex X&#39; Waits.
What is a 'library cache: mutex X' wait? The mutex feature is a mechanism to control access to in me ...
-
Python中关于try...finally的一些疑问
最近看Vamei的Python文章,其中一篇讲异常处理的,原本看完没啥疑惑,或许是自己想的简单了. 看到评论,一个园友的问题引起我的兴趣. 他的问题是 def func(x): try: return ...
-
LeetCode OJ 31. Next Permutation
Implement next permutation, which rearranges numbers into the lexicographically next greater permuta ...
-
jquery开关按钮效果
.circular1{ width: 50px; height: 30px; border-radius: 16px; background-color: #ccc; transition: .3s; ...
-
jenkins git ftp 发布.net 项目
一次搞这个,在其他文章的基础上 添加下自己的 笔记,方便自己查看, -------需要准备的东西--------------- 下载jenkins https://jenkins.io/downloa ...
-
python自动化开发-[第九天]-异常处理、进程
今日概要: 1.异常处理使用 2.进程 3.paramiko模块使用 一.异常处理 1.常见的错误异常 #错误异常一 print(a) #NameError #错误异常二 int('sdadsds') ...
-
intllij IDE 中git ignore 无法删除target目录下的文件
原因: git的本地忽略设置必须保证git的远程仓库分支上没有这个要忽略的文件,如果远程分支上存在这个文件,本地在设置ignore 这个文件,将会失败,无法commit忽略.(有人说是git的bug, ...
-
NumPy 字符串函数
NumPy 字符串函数 以下函数用于对 dtype 为 numpy.string_ 或 numpy.unicode_ 的数组执行向量化字符串操作. 它们基于 Python 内置库中的标准字符串函数. ...