Java编程思想学习笔记(10)

心已入冬 提交于 2020-01-01 18:14:32

Java编程思想学习笔记(10)

内部类

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

创建内部类

例子:


public class Outer {

    class Inner_1{

        private int i = 1;

        public int value(){
            return i;
        }
    }

    class Inner_2{

        private String string;

        Inner_2(String s){
            string = s;
        }

        String getString(){
            return string;
        }
    }

    public void get(String s){

        Inner_1 inner_1 = new Inner_1();

        Inner_2 inner_2 = new Inner_2(s);

        System.out.println(inner_2.getString());
        
    }

    public static void main(String[] args) {

        Outer outer = new Outer();

        outer.get("Hello");

    }

}

创建内部类的方式很简单,就是把类的定义放置于外围类的里面。

当我们在get方法中使用内部类的时候,与使用普通类没什么区别。

更加典型的方法,外部类有一个方法,该方法返回一个指向内部类的引用。


public class Outer_1 {

    class Inner_1{

        private int i = 1;

        public int value(){
            return i;
        }
    }

    class Inner_2{

        private String string;

        Inner_2(String s){
            string = s;
        }

        String getString(){
            return string;
        }
    }

    public Inner_2 get_Inner_2(String s){

        return new Inner_2(s);
    }

    public Inner_1 get_Inner_1(){

        return new Inner_1();
    }


    public void get(String s){

        Inner_1 inner_1 = new Inner_1();

        Inner_2 inner_2 = new Inner_2(s);

        System.out.println(inner_2.getString());

    }


    public static void main(String[] args) {

        Outer_1 outer_1 = new Outer_1();

        outer_1.get("World");

        Outer_1 outer_1_2 = new Outer_1();

        Outer_1.Inner_1 inner_1 = outer_1_2.get_Inner_1();

        Outer_1.Inner_2 inner_2 = outer_1_2.get_Inner_2("Log");
        
    }
    
}

如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,必须像在main()中,具体指明这个对象的类型:OuterClassName.InnerClassName

链接到外部类

当生成一个内部类的对象时,此对象就与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而且不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。

public interface Selector {

    boolean end();
    Object current();
    void next();

}


public class Sequence {

    private Object[] items;

    private int next = 0;

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

//    在序列末增加新的Object
    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++; }
    }

//    获取Sequence中的每一个对象
    public Selector selector() {
        return new SequenceSelector();
    }


    public static void main(String[] args) {
        Sequence sequence = new Sequence(10);
        for(int i = 0; i < 10; i++)
            sequence.add(Integer.toString(i));
        Selector selector = sequence.selector();
        while(!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
    }


}


SequenceSelector是提供Selector功能的内部类。在它的方法中使用了objects,这是一个引用,而且是外围类的一个private字段,然而内部类可以访问其外围类的方法和字段,就像自己拥有它们一样。

当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密的捕获一个指向那个外围类对象的引用,然后,当你访问外围类的成员时,就是用那个引用来选择外围类的成员。

使用this和new

如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this,这样产生的引用自动的具有正确的类型。

public class Outter {

    void f(){

        System.out.println("Outter.f()");

    }

    public class Inner{

        public Outter get_Out(){

            return Outter.this;

        }
    }

    public Inner get_Inner(){

        return new Inner();
    }

    public static void main(String[] args) {

        Outter outter = new Outter();

        Outter.Inner inner = outter.get_Inner();

        inner.get_Out().f();

    }

}

当你想要告知某些其他对象,去创建其某个内部类的对象,要实现的话,就必须在new表达式中提供对其他外部类对象的引用。

public class Outter_1 {

    public class Inner{

        void f(){
            System.out.println("Inner Class");
        }

    }

    public static void main(String[] args) {

        Outter_1 outter_1 = new Outter_1();

        Outter_1.Inner inner = outter_1.new Inner();

        inner.f();

    }

}

要想直接创建内部类的对象,你不能按照你的方式,去引用外部类的名字Outter_1,而是必须使用外部类的对象来创建该内部对象。

在拥有外部类对象之前是不可能创建内部类对象的,这是因为内部类对象会暗暗连接到创建它的外部类对象上,但如果你创建的是嵌套类(静态内部类),那么就不需要对外部类对象的引用。

内部类与向上转型

当内部类向上转型为其基类时,尤其是转型为一个接口时,内部类就有了用武之地。这是因为此内部类,某个接口的实现,能够完全不可见,并且不可用。所得到的只是指向基类或接口的引用,所以能够很方便的隐藏细节的实现、

public interface GetString {

    String getString();
}

public interface GetValue {

    int getValue();
}


现在GetString和GetValue是客户端程序员可以用的接口(接口所有成员自动添加设置为public)

例子:

public class Outter {


    private class GetStrings implements GetString{

        private String string;

        private GetStrings(String string) {
            this.string = string;
        }


        public String getString() {
            return string;
        }
    }

    protected class GetValues implements GetValue{

        private int anInt;

        public int getValue() {
            return anInt;
        }
    }

    public GetString getString(String s){
        return new GetStrings(s);
    }

    public GetValue getValue(){
        return new GetValues();
    }

}


public class Test {

    public static void main(String[] args) {

        Outter outter = new Outter();

        GetString getString = outter.getString("HHe");

        GetValue getValue = outter.getValue();

        String s = getString.getString();

        System.out.println(s);


//        非法 不能访问私有类

//        Outter.GetStrings GetStrings = outter.new GetStrings();


    }
}



在Outter中,内部类GetStrings是private,所以除了Outter外,没有人能够访问它。GetValues是protected,所以只有Outter及其子类,还有与Outter同一个包中的类能够访问它,其他类都不能访问GetValues。这意味着如果客户端程序员想要了解这些成员,那是要受到限制的,实际上,甚至不能向下转型成private内部类(或protected内部类,除非是继承自它的子类)。

在方法和作用域内的内部类

在方法的作用域内,创建一个完整的类,叫做局部内部类


public interface GetString {

    String get_String();


}

public class Outter {

    public GetString getString(String s){

        class GetStrings implements GetString{

            private String string;

            private GetStrings(String string) {
                this.string = string;
            }


            public String get_String() {
                return string;
            }
        }

        return new GetStrings(s);
    }


    public static void main(String[] args) {

        Outter outter = new Outter();

        GetString getString = outter.getString("Hello");

        String s = getString.get_String();

        System.out.println(s);

    }
}


GetStrings是getString方法的一部分,而不是Outter的一部分。所以在getString()之外是不能访问GetStrings的。

在return语句中的向上转型中,返回的是GetString的引用,它是GetStrings的基类。

任意作用域内嵌入一个内部类

public class Parcel6 {

    private void internalTracking(boolean b) {
        if(b) {
            class TrackingSlip {
                private String id;
                TrackingSlip(String s) {
                    id = s;
                }
                String getSlip() { return id; }
            }
            TrackingSlip ts = new TrackingSlip("slip");
            String s = ts.getSlip();
        }
// Can’t use it here! Out of scope:
//! TrackingSlip ts = new TrackingSlip("x");
    }
    public void track() { internalTracking(true); }
    public static void main(String[] args) {
        Parcel6 p = new Parcel6();
        p.track();
    }


}

匿名内部类

public class Outter {


    public GetVal getInner(){
        return new GetVal() {

            private int anInt = 1001;
            public int get_Val() {
                return anInt;
            }
        };

    }


    public static void main(String[] args) {

        Outter outter = new Outter();

        GetVal getVal = outter.getInner();

        System.out.println(getVal.get_Val());
    }

}

getInner方法将返回值的生成与表示这个返回值的类的定义结合在了一起。另外这个类是匿名的,没有名字。

public class Parcel8 {

    public Wrapping wrapping(int x) {
// Base constructor call:
        return new Wrapping(x) { // Pass constructor argument.
            public int value() {
                return super.value() * 47;
            }
        }; // Semicolon required
    }
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(10);
    }



}

public class Wrapping {

    private int i;
    public Wrapping(int x) { i = x; }
    public int value() { return i; }


}



如果基类需要一个有参数的构造器,只需要简单的传递合适的参数给基类的构造器即可。

在匿名内部类末尾的分号,并不是用来标记此内部类结束,实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类而已。

在匿名类中定义字段时,还可以对其进行初始化

public class Parcel9 {

    // Argument must be final to use inside
// anonymous inner class:
    public Destination destination(final String dest) {
        return new Destination() {
            private String label = dest;
            public String readLabel() { return label; }
        };
    }
    public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.destination("Tasmania");
    }


}

如果定义一个匿名类,并且希望使用一个在其外部定义的对象,那么编译器就会要求其参数是final的。

如果想使用一些构造器的行为,应该怎么做?在匿名类中不可能有命名构造器(因为它没有名字),但通过实例初始化,就可以实现为匿名内部类创建一个构造器的效果。

abstract class Base {

    public Base(int i) {
        print("Base constructor, i = " + i);
    }
    public abstract void f();

}



public class AnonymousConstructor {

    public static Base getBase(int i) {
        return new Base(i) {
            { print("Inside instance initializer"); }
            public void f() {
                print("In anonymous f()");
            }
        };
    }
    public static void main(String[] args) {
        Base base = getBase(47);
        base.f();
    }


}

在上例子中,变量i不一定时final的,因为i被传递给匿名类的基类的构造器,并不会在匿名类内部被直接使用。

嵌套类

如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这称为嵌套类。普通的内部类对象隐式的保存了一个引用,指向创建它的外围类对象。然而,当内部类是static时,就不是这样了。

嵌套类意味着:

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

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

嵌套类与普通的内部类还有一个区别,普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类,但是嵌套类可以包含所有这些

public class Parcel11 {

    private static class ParcelContents implements Contents {
        private int i = 11;
        public int value() { return i; }
    }
    protected static class ParcelDestination
            implements Destination {
        private String label;
        private ParcelDestination(String whereTo) {
            label = whereTo;
        }
        public String readLabel() { return label; }
        // Nested classes can contain other static elements:
        public static void f() {}
        static int x = 10;
        static class AnotherLevel {
            public static void f() {}
            static int x = 10;
        }
    }
    public static Destination destination(String s) {
        return new ParcelDestination(s);
    }
    public static Contents contents() {
        return new ParcelContents();
    }
    public static void main(String[] args) {
        Contents c = contents();
        Destination d = destination("Tasmania");
    }


}

接口内部的类

正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,你放到接口中的任何类都自动是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();
        }
        
    }
    
}

如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便。

从多层嵌套类中访问外部类的成员


public class Outter_1 {


    private void f(){
        System.out.println("Outter f");
    }

    class A{

        private void  g(){
            System.out.println("A g");
        }

        public class  B{

            void h(){

                g();

                f();

            }

        }

    }

    public static void main(String[] args) {
        Outter_1 outter_1 = new Outter_1();

        Outter_1.A a = outter_1.new A();

        Outter_1.A.B b = a.new B();

        b.h();
    }

}

一个内部类被嵌套多少层不重要,它能够透明的访问所有它所嵌入的外围类的所有成员

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!