内部类

局部内部类

局部内部类定义在外部类的局部位置,就是代码块和方法内,作用域也是在其定义所在的代码块和方法内。

1.它可以访问外部类的私有成员。

2.不能给局部内部类加访问修饰符,因为它的地位跟局部变量一样,局部变量是不能加访问修饰符的,同理,由于局部变量是可以加final的,所以局部内部类也可以加final,并且外部其他类访问不到局部变量,所以也是访问不到已创建的内部类的

3.局部内部类想访问外部类的成员,直接访问即可,而外部类想要访问局部内部类的成员时,要在作用域内,创立局部内部类的对象,然后才能访问

4.当外部类的成员与局部内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名

5.局部内部类可以嵌套

 

匿名内部类

有时开发中某些类只用到一次,那么如果特意写一个类的话会很不划算,这时匿名内部类就派上用场了,可以很好地简化开发

 

匿名内部类也定义在外部类的局部位置中。

 

语法:new  类名/接口名(参数列表){

                     //类体

                   } ;

 

匿名内部类注意:

①本质之一是类,且是内部类,匿名内部类显式匿名,隐式有名,就是说你不能直接看到它的名字,需要用方法来获取,如下列代码

 

public class Test {
    public static void main(String[] args) {
        new B().getAnonymousClassName();
    }
}

interface AA {
void introduce();
}


class B {
    public void getAnonymousClassName() {
        AA kzh = new AA() {
            @Override
            public void introduce() {
                System.out.println("大家好,我叫kzh,目标是天天向上,天天进步");
            }
        };
        kzh.introduce();
        //获取匿名内部类的类名
        System.out.println("kzh的运行类型是" + kzh.getClass());
    }
}


运行结果

大家好,我叫kzh,目标是天天向上,天天进步
kzh的运行类型是class com.kzh.test.B$1

 

 

============当再次加一个外部类==============

public class Test {
    public static void main(String[] args) {
        new A().callB();
    }
}


    class A {
        class B {
            public void getAnonymousClassName() {
                AA kzh = new AA() {
                    @Override
                    public void introduce() {
                        System.out.println("大家好,我叫kzh,目标是天天向上,天天进步");
                    }
                };
                kzh.introduce();
                //获取匿名内部类的类名
                System.out.println("kzh的运行类型是" + kzh.getClass());
            }
        }

        public void callB() {
            new B().getAnonymousClassName();
        }
    }

运行结果
大家好,我叫kzh,目标是天天向上,天天进步
kzh的运行类型是class com.kzh.test.A$B$1
 

 

 

======探究$后面的数字是否是序号以及序号的排列方式======

public class Test {
    public static void main(String[] args) {
        new A().callB();
    }
}


class A {
    class B {
        public void getAnonymousClassName() {
            AA kzh = new AA() {
            };
            System.out.println("kzh的运行类型是" + kzh.getClass());
        }

        class C {
            public void getAnonymousClassName() {
                AA pg = new AA() {
                };
                AA wyt = new AA() {
                };
                //获取匿名内部类的类名
                System.out.println("wyt的运行类型是" + wyt.getClass());
                System.out.println("pg的运行类型是" + pg.getClass());
            }
        }

        public void callC() {
            new B.C().getAnonymousClassName();
        }

    }

    public void callB() {
        new B().getAnonymousClassName();
        new B().callC();
    }
}


运行结果
kzh的运行类型是class com.kzh.test.A$B$1
wyt的运行类型是class com.kzh.test.A$B$C$2
pg的运行类型是class com.kzh.test.A$B$C$1

 

可以看出,匿名内部类的类名的格式是:外部类名$外部类名$.......外部类名$ + 序号(此匿名内部类在最近一级外部类的序号,按定义顺序来排)。

jdk底层在创建匿名内部类后,就立刻创建其实例,并且将地址返回给栈的对象名,然后这个匿名内部类就不能使用了。

 

当匿名内部类用new 类名时,能加新普通属性√,新普通方法√,构造器×,普通代码块√,可以重写和重载方法

 

对于new 类名{},如果想在{ }内建立匿名内部类,则必须要有引用指向,因为底层是匿名内部类继承new后面的类,那么按照继承的规则,肯定要优先完成父类的初始化

 而这个{ }里面建立的匿名内部类(下面代码中的c)是在匿名内部类的父类里的,那么在初始化的时候完成建立,这时如果你没有引用指向它,那么将找不到匿名内部类c,所以必须要有一个引用

 

②、③如下面代码

 

public class Test {
    public static void main(String[] args) {
        A a = new A() {
             int a;

            static int b;//报错,jdk8不支持内部类的static

            @Override
            public void a(){
                System.out.println("call  a()");
            }//注意,如果是抽象类,要遵循抽象类的规则,即一定要重写类中的抽象方法

public void a(int a){}
public void b(){} {} static {}//报错,jdk8不支持内部类的static public A(){}//报错,并且new A()的形参列表会传给A类的构造器 new A(){};//报错
      
A c = new A(){};
}; System.out.println("a的运行类型是" + a.getClass()); //a的运行类型是class com.kzh.test.Test$1,而不是以new后面的类为运行类型 //因为在底层,匿名内部类会继承new后面的类 //匿名内部类的类名与new后面的类名无关,只与匿名内部类定义位置所在的外部类有关

} } class A { public A() { } public void a(){ System.out.println("CALL a()"); } }

 

匿名内部类不仅是一个类,也是一个对象,它可以直接访问成员

public class Test {
    public static void main(String[] args) {
          new A(){ //这里依然可以写东西 }.a();//正确
  }
}

class A {
    public A() {
    }
    public void a(){
        System.out.println("CALL a()");
    }

 

⑤指向匿名内部类的引用,仍然是遵循向上转型和向下转型的规则,即必须要有继承关系

public class Test {
    public static void main(String[] args) {
        B a = new A() {};//报错 
     
        A b = new B() {};//报错
    }
}

class A{}

class B{}

 

⑥和局部内部类一样,匿名内部类不可以加修饰符,作用域仅在局部位置,并且注意,如果用引用来指向匿名内部类,依然遵守向上转型和向下转型的规则,即只能访问编译类型的所有成员

interface Computer {
}

public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer() {
            int a;
        };
        System.out.println(computer.a);//报错,computer的编译类型是Computer

System.out.println(new Computer() { int a; }.a);//正确,等效于是编译类型是这个匿名内部类的引用在访问特有变量a } }

 

⑦外部其他类不能使用已创建的匿名内部类,因为匿名内部类在局部位置,用完就没,而外部类想要访问匿名内部类的成员时,要在作用域内,创立局部内部类的对象,然后才能访问

 

⑧当外部类的成员与匿名内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名

 

匿名内部类一般的应用场景

①当想创建一个只用一次的类时,比如作为一个形参

 

②如果想更改某个类的对象{  }里的内容时不会影响到其他对象,直接匿名内部类继承该类,就可以达成这个目的。

 

③如果某些类或接口的方法每次调用时都可能要更改方法体(比如方法每次需要实现的功能都可能不同时),那么直接匿名内部类就可以安全(不影响到下次该方法的使用),便捷又简洁(不用特意去另一个类重写或者在类里多添加一个方法)地帮你达成这个目的

 

 

成员内部类

1.定义在外部类的全局位置上

2.可以直接访问外部类的所有成员

3.可以添加访问修饰符和final,但不能加static

4.外部其他类可以访问已创建的成员内部类

5.当外部类的成员与成员内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名

 

 

静态内部类

1.定义在外部类的全局位置上

2.可以直接访问所有的静态成员,且不能访问任何非静态成员

3.可以加任意访问修饰符和final,并且必须要有static

4.当外部类的成员与静态内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.成员名(一般用这个)/外部类对象.成员名

5.外部其他类可以访问已创建的静态内部类

 

posted @ 2022-03-10 21:52  codemelo  阅读(77)  评论(0)    收藏  举报