热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《Java编程思想》之内部类——深奥而复杂

1、当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地(从实现某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样得。)这是因为此内部类——

1、当将内部类向上转型为其基类,尤其是转型为一个接口的时候,内部类就有了用武之地(从实现某个接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样得。)这是因为此内部类——某个接口的实现——对于其他人来说能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便地隐藏实现细节

interface Destination{
String readLabel();
}
interface Contents{
int value();
}
class Parce1{
private class PContents implements Contents{
private int i = 2012;
public int value() {
return i;
}
}
protected class PDestination implements Destination{
private String label;
private PDestination(String label){
this.label = label;
}
public String readLabel() {
return label;
}
}

public Destinationdest(String label){
return new PDestination(label);
}
public Contents cont(){
return new PContents();
}
}
public class TestParcel{
public static void main(String[] args){
Parce1 p = new Parce1();
Contents c = p.cont();
System.out.println(c.value());
Destination d = p.dest("Cannel_2020");
System.out.println(d.readLabel());
}
}

运行结果:


1).PContent是private的,所以除了Parcel,没有人能访问它。

2).Pdestination是protected,所以只有Parcel及其子类、还有与Parcel用包的类能访问。

3).这意味着,如果客户端程序员想了解或访问这些成员,那是要受限制的。

4).实际上,甚至不能向下转型成private内部类(或protected内部类,除非是继承自它的子类),因为不能访问其名字。

5).所以通过类似如上的方式,可以完全阻止任何依赖类型的编码,并且完全隐藏了实现的细节

6).此外,从客户端程序员的角度来看,由于不能访问(为什么?)任何新增加的、原本不属于公共接口的方法所以扩展接口是没有价值的。这也给Java编译器提供了生成更高效代码的机会。

7).以上是内部类的典型用途。

 

2、在一个方法或者任意的作用域内定义内部类的两个理由:

1).如前所示,你实现了某个类型的接口,于是可以创建并返回对其的引用。。

2).你要解决一个复杂的问题,想创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。

3、局部内部类:在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类。方法的执行完毕,并不意味该局部内部类就不可用了。另外,局部内部类不能有访问说明符。

4、匿名内部类

public Contentscont(){
return new Contents(){
private int i = 2012;
public int value(){
return i;
}
};
}

其上是下述形式的简化形式:

public Contents cont(){
class MyContents implements Contents{
private int i = 2012;
public int value(){
return i;
}
}
return new MyContents();
}

若匿名内部类需要一个有参数的构造器

class Wrapping{
int i = 0;
Wrapping(int i){
this.i = i;
}
public int value() {
return i;
}
}
public class Parcel{
public Wrapping wrap(int i){
return new Wrapping(i){
public int value(){
return super.value()*10;
}
};
}
public static void main(String[] args){
Wrapping w = new Parcel().wrap(2012);
System.out.println(w.value());
}
}

5、匿名内部类没有命名的构造器,但通过实例初始化,能够达到为一个构造器的效果

abstract class Base{
public Base(int i){
System.out.println("基类的构造函数内, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor{
public static Base getBase(int i){
return new Base(i){
{
System.out.println("实例初始化器内");
}
public void f(){
System.out.println("在匿名内部类里的f()");
}
};
}
public static void main(String[] args){
Base base = getBase(2012);
base.f();
}
}

运行结果:

 

6、匿名内部类中使用一个在外部定义的对象,那么编译器会要求其参数引用是final的。

7、Java的内部类,拥有其外围类的所有元素的访问权。(这与C++的嵌套类的设计非常不同,在C++中只是单纯的名字隐藏机制与外围对象没有联系,也没有隐含的访问权。)

这是如何做到的呢?

       当外围类的对象创建了一个内部类的对象时,此内部类对象必定会保存一个指向那个外围类对象的引用。在你访问外围类的成员的时候,就是那个“隐藏的”引用来选择外围类的成员。(构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。)

8、嵌套类:如果不需要内部类对象与其外围类对象之间的联系,那么可以将内部类声明为static。这通常称为嵌套类

1).要创建嵌套类的对象,并不需要其外围类的对象。

2).不能从嵌套类的对象中访问非静态外围对象。

9、普通内部类与嵌套类的区别:普通内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。

10、普通内部类(非static)可以通过一个特殊的this引用可以链接到其外围类对象。嵌套类就没有这个特殊的this引用,这使得它类似一个static方法。

11、内部类的继承存在的问题:那个指向外围类对象的“秘密的”引用必须被初始化,而在导出类中不再存在可连接的缺省对象。例子如下:

class Outter{
Outter(){
System.out.println("Outter()");
}
class Inner{
Inner(){
System.out.println("Inner()");
}
}
}
public class InheritInner extends Outter.Inner{
InheritInner(Outter outter){
outter.super();
System.out.println("InheritInner()");
}
public static void main(String[] args){
Outter outter = new Outter();
InheritInner inheritInner = new InheritInner(outter);
}
}

       虽然是继承内部类,但是当生产一个构造器时,缺省的构造器并不算好,而且不能只是传递一个指向外围类对象的引用,必须在构造器内使用如下语法:

       outter.super();

这样才提供了必要的引用,然后程序才能编译通过。若缺省上面语句,会提示“由于某些中间构造函数调用,没有任何类型 Outter 的外层实例可用”的错误。

12、内部类的覆盖,比较俩个例子:

1).

class Outter{
private Inner inner;
Outter(){
System.out.println("基类Outter()");
inner = new Inner();
}
protected class Inner{
Inner(){
System.out.println("基类的内部类Inner()");
}
}
}
public class InheritInner extends Outter{
public class Inner{
public Inner(){
System.out.println("InheritInner重新定义的内部类Inner()");
}
}
public static void main(String[] args){
new InheritInner();
}
}

运行结果

 

2).

class Outter{
private Inner inner;
Outter(){
System.out.println("Outter()");
inner = new Inner();
}
public void insertInner(Inner inner){
this.inner = inner;
}
public void outterFunc(){
inner.func();
}
protected class Inner{
Inner(){
System.out.println("Outter.Inner()");
}
public void func(){
System.out.println("Outter.Inner().func()");
}
}
}
public class InheritInner extends Outter{
public InheritInner(){
System.out.println("InheritInner()");
//下面语句先Outter.Inner(),再是InheritInner.Inner();
Inner inner = new Inner();
insertInner(inner);
}
public class Inner extends Outter.Inner{
public Inner(){
System.out.println("InheritInner.Inner()");
}
public void func(){
System.out.println("InheritInner.Inner().func()");
}
}
public static void main(String[] args){
Outter outter = new InheritInner();
outter.outterFunc();//动态绑定
}
}

运行结果:


下接《Java编程思想》之为什么需要内部类?

以上内容整理自《Java编程思想》,若有遗漏,请您不吝指出!



推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 从零学Java(10)之方法详解,喷打野你真的没我6!
    本文介绍了从零学Java系列中的第10篇文章,详解了Java中的方法。同时讨论了打野过程中喷打野的影响,以及金色打野刀对经济的增加和线上队友经济的影响。指出喷打野会导致线上经济的消减和影响队伍的团结。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了在Java中gt、gtgt、gtgtgt和lt之间的区别。通过解释符号的含义和使用例子,帮助读者理解这些符号在二进制表示和移位操作中的作用。同时,文章还提到了负数的补码表示和移位操作的限制。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
author-avatar
陈浩颖美娇承湖_527
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有