资讯专栏INFORMATION COLUMN

(Thinking in Java)第10章 内部类

Brenner / 685人阅读

摘要:内部类中也可以取得这个外部类对象引用。创建成员内部类对象的时候需要外部类对象。另外在方法中的内部类不能加等权限修饰符,只能加和修饰符。可以在接口内部定义内部类,而且他们即使没有修饰,也会自动变成的。

Thinking in Java捞干货,写笔记 一、成员内部类 1.最基本使用
public class Demo {
    class Contents{
        private int i=11;
        public int value(){
            return i;
        }
    }

    class Destination{
        private String label;
        Destination(String whereTo){
            label=whereTo;
        }
        String readLabel(){
            return label;
        }
    }
    
    public void ship(String dest){
        Contents c=new Contents();
        Destination d=new Destination(dest);
        System.out.println(d.readLabel());
    }

    public static void main(String[] args) {
        Demo d=new Demo();
        d.ship("Tasmania");
    }
}
2.内部类可以访问外部类的成员

内部类可以访问外部类的成员变量。如下:

public class Demo {
    private Object[] items;
    private int next = 0;

    public Demo(int size) {
        items = new Object[size];
    }

    public void add(Object x) {
        if (next < items.length) {
            items[next++] = x;
        }
    }

    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) {
        Demo d = new Demo(10);
        for (int i = 0; i < 10; i++) {
            d.add(Integer.toString(i));
        }
        Selector selector = d.selector();
        while (!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
    }
}

interface Selector {
    boolean end();

    Object current();

    void next();
}

因为在创建内部类对象的时候,内部类对象会捕获一个指向外部类对象的引用。访问外部类成员的时候,就是用这个引用来获取外部类成员的。内部类中也可以取得这个外部类对象引用。举例如下:

public class DotThis{
    void f(){
        System.out.println("DotThis.f()");
    }
    class Inner{
        public DotThis outer(){
            return DotThis.this;
            //A plain "this" would be Inner"s this
        }
    }

    public Inner inner(){
        return new Inner();
    }

    public static void main(String[] args) {
        DotThis dt=new DotThis();
        DotThis.Inner dti=dt.inner();
        dti.outer().f();
    }
}

当要在其他类中创建一个内部类对象的时候,可以使用.new语法。

public class DotNew{
    public class Inner{
    }
    public static void main(String[] args){
        Inner dni=new DotNew().new Inner();
    }
}

当创造内部类对象的时候,如果这个内部类不是嵌套类(静态内部类),那么就必须要通过外部类对象,才能创建这个内部类对象,为什么呢,之前说过,因为这个内部类对象要获取外部类的引用啊。
并且在存在多个内部类的时候,多个内部类之间可以互相创建对象。例子就不举了。

小结:现在所说的都是成员内部类,其实内部类没那么复杂,既然叫做成员内部类了,他就只是类的成员罢了。他也可以带修饰符,他的修饰符和其他普通的成员变量的修饰符的意义也没有什么不同。

3.内部类权限修饰符

当内部类被private修饰的时候,该类只能被外部类内的方法使用,其他类不能获取该内部类的引用,因为是private的,所以其他类根本不知道存在一个这样的类。当然也可以作为一个成员变量使用,但是如果作为成员变量,则其他类并不能直接创建内部类的引用,需要用其他手段获取该引用,如下:

class Outer{
    Inner in;
    private class Inner implements a_interface{
        void show(){
            System.out.println("123");
        }
    }
}
interface a_interface{
    void show();
}

class test{
    //Inner in=new Outer().in;这是错误的,因为test并不知道Outer类有一个Inner内部类,因为是私有的
    a_interface in=new Outer().in;//可以运用向上转型的方法获取private修饰的内部类。
}

小结:其实这也很好记,无论是public还是private,修饰到内部类上的时候,和他们修饰普通的成员变量(如string,int之类)的时候没什么不同,规则都一样,public就都能使用,private就类内可以用。所以规则就记住三条就好:1.先考虑外部类的权限,是否可以获取一个外部类对象。2.创建成员内部类对象的时候需要外部类对象。3.考虑内部类的权限,是否可以获取这样的一个内部类对象(或者说,在外部知不知道有这样一个内部类)。

二、方法和作用域内的内部类

当我们需要解决一个复杂的问题,想创建一个类来辅助解决问题,但是不希望这个类是公共可用的,甚至不希望在外部类之内的其他地方可以访问到这个辅助类。我们可以运用方法内的内部类

public class Outer {
    public InterfaceDemo get_InterfaceDemo(String s) {
         class InterfaceDemoTool implements InterfaceDemo {
            private String label;

            private InterfaceDemoTool(String label) {
                this.label = label;
            }

            public String readLabel() {
                return label;
            }
        }

        return new InterfaceDemoTool(s);
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InterfaceDemo i = o.get_InterfaceDemo("123");
    }
}

interface InterfaceDemo {
    String readLabel();
}

当然在方法中还可以定义多个内部类,并且这些内部类之间的关系和普通一个Java文件中多个类之间的关系好像没什么不同。也可以相互继承和创建对象。另外在方法中的内部类不能加private等权限修饰符,只能加abstract和final修饰符。
另外也可以在某个作用域内创建内部类对象

if(a==b){
     class inner{
     }
     new inner();
}
三、匿名内部类

下面这块代码中get_inner()的意思是,创建一个继承自InnerFather的匿名类对象,并且自动向上转型为InnerFather后返回。

public class Outer {
    public InnerFather get_inner() {
        return new InnerFather() {
            void print(){
                System.out.println("Inner_Override");
            }
        };
    }

    class InnerFather {
        InnerFather() {

        }
        void print(){
            System.out.println("InnerFather");
        }
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InnerFather i = o.get_inner();
        i.print();
    }
}

当然这只是有无参构造函数,当父类只有一个含参构造函数的时候,我们可以这样向匿名内部类传入一个构造函数参数。

public class Outer {
    public InnerFather get_inner(int i) {
        return new InnerFather(i) {
            void print(){
                System.out.println("Inner_Override");
            }
        };
    }

    class InnerFather {
        InnerFather(int i) {

        }
        void print(){
            System.out.println("InnerFather");
        }
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        InnerFather i = o.get_inner(10);
        i.print();
    }
}

可以通过构造代码块来实现匿名内部类的自定义的构造函数

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor");
    }

    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i){
            {System.out.println("AnonymousConstructor constructor");}
            public void f(){
            }
        };
    }

    public static void main(String[] args) {
        Base base = getBase(47);
    }
}

(书上说,如果传入了新的对象,就比如下面例子中的s_in,这个s_in就必须是final的,但是我实验了一下发现并不用啊,我也没太搞懂。)

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor");
    }

    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i,String s_in){
        return new Base(i){
            {System.out.println("AnonymousConstructor constructor");}//构造代码块4.嵌套类
            String s=s_in;
            public void f(){
            }
        };
    }

    public static void main(String[] args) {
        Base base = getBase(47,"hello");
    }
}
四、嵌套类

嵌套类指的是被static修饰的内部类。这意味着:1.创建嵌套类对象不需要外部类对象。2.不能再嵌套类对象之中访问非静态的外围类对象。普通内部类的成员和方法只能放在类的外部层次上(这句话我没搞懂= =),所以普通内部类不能有static的成员和方法。但是嵌套类可以有。

public class Outer {
    static class Inner{
        static int i=5;
    }
    public static void main(String[] args) {
        Inner i=new Outer.Inner();
    }
}

可以从上面的例子看到,在创建这个嵌套类对象的时候,并没有像最开始那样,用一个外部类对象来创建这个内部类对象。其实这和静态方法差不多。

可以在接口内部定义内部类,而且他们即使没有static修饰,也会自动变成public static的。

public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface{
        public void howdy(){
            System.out.println("howdy!");
        }
        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}

书上有句话说的很好,在开发的时候建议在每个类中都写一个main方法测试,但是这又必须带着那些已经编译过的额外代码,所以我们可以用嵌套类放置测试代码。

public class Outer {
    public void f(){
        System.out.println("I need to be tested");
    }

    public static class Tester{
        public static void main(String[] args) {
            Outer o=new Outer();
            o.f();
        }
    }
}

当然以上两段代码如果是在eclipse上运行的话,需要设置一下运行的main函数在哪,否则会报错

五、多层内部类嵌套

纸老虎,爱有几层有几层,反正只要是外部类的东西,不管哪层外部类,都能访问到。

public class Outer {
    void f(){
        System.out.println("hello");
    }
    class Inner1{
        void g(){
            System.out.println("java");
        }
        class Inner2{
            void h(){
                f();
                g();
            }
        }
    }
    public static void main(String[] args) {
        Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
        in2.h();
    }
}

下面是个好玩的

public class Outer {
    void f(){
        System.out.println("hello");
    }
    class Inner1{
        void f(){
            System.out.println("java");
        }
        class Inner2{
            void h(){
                f();
            }
        }
    }
    public static void main(String[] args) {
        Outer.Inner1.Inner2 in2=new Outer().new Inner1().new Inner2();
        in2.h();
    }
}

最后打印结果是java。

大概就这么多吧,以后如果还有新东西再补。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67513.html

相关文章

  • Thinking in Java13 字符串

    摘要:四上的操作看五格式化输出运用和语言很相似和是等价的哟类格式化说明符转换六正则表达式网上教程学七扫描输入新增了类。 一、不可变String String类型的对象是不可变的,所有的改变实际上都是创建了一个新的String对象,另外当String作为传入参数的时候,其实实际上传入的是这个引用的一个拷贝,这个方法结束了之后这个传入的引用也就消失了,原来的那个String不会受到方法内的影响而...

    feng409 评论0 收藏0
  • Thinking in Java9 接口

    摘要:但如果导出类还有抽象方法,那这个类还应该加上声明为抽象类。并且接口具有继承的一系列特点,如向上转型等等。接口中的方法是自动是的。 Thinking in Java 好书全是干货 一、抽象类和抽象方法 抽象方法:这种方法只有声明而没有方法体,下面是抽象方法生命所采用的语法 abstract void f(); 包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为...

    CoorChice 评论0 收藏0
  • Thinking in Java14 型信息

    摘要:通过运行时类型信息,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。编程应该尽量面向接口编程,应该对类型信息尽量的少了解二对象看书,书上写得好静态语句块在这个类被加载的时候运行。 一、为什么需要RTTI Run-Time Type Information。通过运行时类型信息,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。编程应该尽量...

    tomorrowwu 评论0 收藏0
  • Thinking in Java12 通过异常处理错误

    摘要:异常处理程序抛出的异常必须在异常处理程序中得到处理。终止与恢复异常处理有两种模型,支持终止模型,一旦异常被抛出,表明错误无法挽回,无法退回来继续执行之前出错的代码。对于异常来说,最重要的部分就是类名。 一、概念 使用异常能降低处理错误代码的复杂程度,并且将错误在一个地方进行处理,于是将描述在正常行为过程中做过什么事的代码和出了问题怎么办的代码相分离 二、基本异常 异常情形指的是当前环境...

    miguel.jiang 评论0 收藏0
  • Thinking in Java11 持有对象

    摘要:迭代器解决了这个问题。删除后于是我们可以写一个方法,接受一个类型,然后让他调用方法,这就不需要考虑这个是个还是了,也就是说,可以将遍历容器的操作与序列底层的结构分离,迭代器统一了对容器类的访问方式。十二和两种遍历的方法,与迭代器方法。 一、泛型和类型安全的容器 package tij.hoding; import java.util.ArrayList; public class ...

    v1 评论0 收藏0

发表评论

0条评论

Brenner

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<