zoukankan      html  css  js  c++  java
  • java 内部类、匿名内部类

    一:内部类

    1:什么是内部类?
      大部分时候,类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有些地方也叫做嵌套类),包含内部类的类也被称为外部类(有些地方也叫做宿主类)

    我们先创建一个基本的内部类结构:

    class Outer{//外部类
        //内部类
        class Inner{
            
        }
    }

    2:内部类的划分

    内部类分为成员内部类局部内部类。内部类也会生成.class文件。

    2.1: 成员内部类

      定义在外部类中的成员位置,与类中的成员变量相似,可通过外部类对象进行访问。

     内部类可以使用外部类的成员,包括私有成员。但是外部类要使用内部类的成员,必须建立内部类变量。

    2.2: 局部内部类(比较少用)

    定义:在方法里面有一个内部类。

     只有在内部类所属的方法中创建内部类对象,方可访问局部内部类。而测试类中只需要创建外部类对象,然后调用外部类方法即可。

    3:例子

    import java.util.HashMap;
    
    public class Parcell {
    
        private HashMap<String, String> testMap = new HashMap<String, String>();
    
        class Contents {
            // 返回一个外部类的引用.
            public Parcell ParcellRef = Parcell.this;
        }
    
        class Destination {
            public void putSomethingInMap() {
                testMap.put("hello", "world");
                System.out.println(testMap.get("hello"));
            }
            
        }
    
        public Destination to() {
            return new Destination();
        }
    
        public Contents contents() {
            return new Contents();
        }
    
        public void ship(String dest) {
            Contents c = new Contents();
            Destination d = new Destination();
        }
    
        public static void main(String[] args) {
            Parcell p = new Parcell();
            Parcell.Contents c = p.contents();
            Parcell.Destination d = p.to();
            d.putSomethingInMap();
            Parcell.Contents c1 = p.new Contents();
        }
    
    }

    内部类的语法介绍

      (1)普通内部类持有一个指向外部类的引用。要创建普通内部类,一定要先创建外部类。
      (2)普通内部类就像人体的心脏一样,能够随意访问外部类的任意成员变量。
      (3)在内部类中可以通过“外部类类名.this”的方式返回一个指向外部类实例的引用.如Parcell.this
      (4)在外部类的static方法中若要创建内部类对象,则需要通过“外部类类名.new XXX()”的方式来创建。
      (5)普通内部类中不能拥有静态成员变量。静态内部类中可以拥有静态成员变量。也可以拥有非静态成员变量。但是静态内部类不能访问外部类中非静态的成员变量。而普通内部类可以访问外部类的静态成员变量。

    为什么static方法中需要p.new XXX()的方式而非static方法中我们直接new 内部类名 就可以创建一个对象了呢?
    如果你有这样的疑问请再看看第一条,一定可以想明白的。

    4.作用
      1)更好的封装性
      2)内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,但外部类不能访问内部类的实现细节,例如内部类的成员变量
      3)匿名内部类适合用于创建那些仅需要一次使用的类

    体外话:静态内部类

      Java里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。被static修饰的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。

    静态内部类的特点:

      1.非静态内部类中不允许定义静态成员
      2.外部类的静态成员不可以直接使用非静态内部类
      3.静态内部类,不能访问外部类的实例成员,只能访问外部类的类成员

    二:匿名内部类

    匿名内部类使用最频繁的场合就是在创建线程的时候。

    程序清单2-1:

    public class Demo {
    
        public void test(String title) {
            Thread thread = new Thread(new Runnable() {
    
                @Override
                public void run() {
                    // title = "我不要吃鸡";
                    // 改变时会提示错误
                    // 在封闭范围中定义的局部变量必须是final的。
                    System.out.println(title);
                }
            });
            thread.start();
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Demo demo = new Demo();
                demo.test("我要吃鸡" + i);
            }
        }
        
    }
        在程序清单2-1中,test()方法内部有一个线程对象thread,是通过new Thread()创建的。new Thread()可以接收一个实现了Runnable接口类型的对象,这个对象要怎么创建呢?可以通过匿名内部类的形式来创建——new Runnable() {public void run(){......}}——这段简短的代码等同于:
    // 实现Runnable接口
    class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            
        }
    }
    
    // 向上转型
    Runnable myRunnable = new MyRunnable();

    匿名内部类的好处就在于不仅节省了定义实现类的过程,还能够自动向上转型

      在程序清单2-1中,test()方法还有一个参数title,JDK1.8之前,编译器要求它必须是final类型的。但JDK1.8之后,如果我们在匿名内部类中需要访问局部变量,那么这个局部变量不再需要用final关键字修饰了。

      但如果想要在匿名内部类中改变局部变量的值,编译器就会提醒你不能这样做,它会提示:“在封闭范围中定义的局部变量必须是final的。”

    另一个关于匿名内部类的例子:

    开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作。

       1、定义子类

       2、重写接口中的方法

       3、创建子类对象

       4、调用重写后的方法

    我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

    条件

      匿名内部类必须继承一个父类或者实现一个父接口

    格式

    new 父类名或者接口名(){
        // 方法重写
        @Override 
        public void method() {
            // 执行语句
        }
    };

    使用方式

    以接口为例,匿名内部类的使用,代码如下:

    定义接口:

    public abstract class FlyAble{
        public abstract void fly();
    }

    创建匿名内部类,并调用:

    public class InnerDemo {
        public static void main(String[] args) {
            /*
            1.等号右边:是匿名内部类,定义并创建该接口的子类对象
            2.等号左边:是多态赋值,接口类型引用指向子类对象
            */
            FlyAble  f = new FlyAble(){
                public void fly() {
                    System.out.println("我飞了~~~");
                }
            };
    
            //调用 fly方法,执行重写后的方法
            f.fly();
        }
    }

    通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:

    public class InnerDemo2 {
        public static void main(String[] args) {
            /*
            1.等号右边:定义并创建该接口的子类对象
            2.等号左边:是多态,接口类型引用指向子类对象
           */
            FlyAble  f = new FlyAble(){
                public void fly() {
                    System.out.println("我飞了~~~");
                }
            };
            // 将f传递给showFly方法中
            showFly(f);
        }
        public static void showFly(FlyAble f) {
            f.fly();
        }
    }

    以上两步,也可以简化为一步,代码如下:

    public class InnerDemo3 {
        public static void main(String[] args) {           
            /*
               创建匿名内部类,直接传递给showFly(FlyAble f)
            */
            showFly( new FlyAble(){
                public void fly() {
                    System.out.println("我飞了~~~");
                }
            });
        }
    
        public static void showFly(FlyAble f) {
            f.fly();
        }
    }

    为什么需要内部类?

      Java的内部类让我很容易的想起来JavaScript的闭包,闭包就是定义在一个函数内部的函数——这听起来和Java的内部类定义一样一样的。本质上,闭包是将函数内部与函数外部连接起来的桥梁。内部类一样,它是将内部类与外部类连接起来的桥梁。

    来看看什么是闭包吧:

    function wanger() {
        var age = 30;
        function know() {
            console.log(age);
        }
    }
    
    wanger();
    // 控制台输出30

    除此之外,内部类最引人注意的原因是:

     内部类可以独立地继承一个抽象类或者实现一个接口,无论外部类是否也这样做了,对内部类都没有影响。

  • 相关阅读:
    Postman使用
    Java-性能调优-理解GC日志
    Java-性能调优实战(jps、jstack)
    Java-性能调优工具-jstat
    Java-内存模型
    生产者消费者JAVA实现
    Java-GC垃圾收集器
    JAVA-JVM垃圾回收算法
    Java线程状态流转---线程
    【洛谷5072】[Ynoi2015] 盼君勿忘(莫队)
  • 原文地址:https://www.cnblogs.com/myseries/p/12073705.html
Copyright © 2011-2022 走看看