美文网首页
[Java基础] 外部类, 内部类

[Java基础] 外部类, 内部类

作者: LCN233 | 来源:发表于2019-10-01 17:06 被阅读0次
    Alt 'Java 类'

    外部类

    我们在创建类的时候,创建的 .java 文件(源文件)的文件名必须和我们要创建的类的名字一样。而这个我们创建的类,就是外部类, 也叫顶级类。

    做一点引申:

    1. 在一个源文件里面,可以有很多个顶级类, 但是这些顶级类只有和文件名一样的顶级类才能被 public 修饰, 只有一个,其他的都只能用默认(什么都不加)进行修饰。
    2. 同一个源文件里面的多个顶级类,因为只有一个可以被 public 修饰,所以导致其他的顶级类,只能在当前包路径下使用。
    3. 《Effective Java, Third Edition》第25条建议: 每个源文件只对应一个顶级类

    内部类

    又称之为嵌套类,是在类中在定义另外一个类,这个类可以在顶级类里面的任意地方

    1. 静态内部类

    public class OutClass {
        public static class InnerClass {  
        }
    }
    

    直接在顶级类的内部声明, 通过 static 进行修饰的类

    特点:

    1. 所有的权限修饰符都可以进行修饰
    2. 能够在类能定义静态属性和方法
    3. 只能使用外部类的静态属性和方法
    4. 在其他类里面调用静态内部类(静态内部类与外部类没有关系, 在编译后,就是2个普通的类)
    OutClass.StaticInnerClass innerClass = new OutClass.StaticInnerClass();
    

    2. 非静态内部类

    1. 成员内部类

    public class OutClass {
        public class InnerClass {
        }
    }
    

    在顶级类的直接内部,和类的属性同级

    特点:

    1. 所有的权限修饰符都可以进行修饰
    2. 不能够在类内定义静态属性和方法
    3. 能够访问外部类的所有属性和方法, 包括静态的
    4. 在其他类里面调用成员内部类(成员内部类对象会隐式的引用一个外部类对象, 也就是2者之间有联系)
    OutClass outClass = new OutClass();
    // 需要先创建出外部类的实例,通过实例才能创建内部类
    OutClass.InnerClass innerClass = outClass.new InnerClass();
    

    2. 局部内部类

    public class OutClass {
       public void fn() {
           class InnerClass {
           }
       }
    }
    

    声明在顶级类的某个作用域内(方法体内, if 判断里面等)

    特点:

    1. 这个类只能使用默认权限修饰符, 不能被其他的权限修饰符修饰
    2. 不能够在类内定义静态属性和方法
    3. 可以访问到外部类的属性和方法,包括静态的
    4. 在其他类是无法调用到局部内部类的
    5. 局部类只能在对应的作用域起作用, 超出了对应的作用域就没了,所以无法被其他的类调用

    3. 匿名内部类

    public class OutClass {
        public void fn() {
           /**
            *     创建格式:
            *     new 父类/接口(参数列表) {
            *     }
            */
            new Thread(new Runnable() {
              @Override
              public void run() {
              }
            });
        }
    }
    

    在顶级类的某个作用域内, 直接创建使用, 没有进行具体的声明

    特点:

    1. 匿名内部类为局部内部类的特例, 具备局部内部类的特点

    内部类如何访问外部类的同名方法/属性

    内部类的属性, 方法和外部类的重名了,在调用时,优先使用内部类的, 同时可以通过外部类.this.方法/属性,访问到外部类的同名方法/属性

    局部内部类访问方法体内入参/变量的条件

    1. 局部内部类可以访问方法内的局部变量和入参,但是前提是这个变量和入参在这个方法体内是被 final 修饰过的。
    2. 在 jdk8 中, 可以直接声明后使用,只要你保证你的入参/变量满足 Effectively final

    注: Effectively final 是 jdk8 新增的一个特性。(对于一个变量,如果没有给它加final修饰,而且没有对它的二次赋值,那么这个变量就是effectively final(有效的不会变的)。简单的来说,我们在方法 fn 里面声明了一个 变量 int a = 1; 在这个方法体内,除了第一次声明外,没有别的地方对其进行了修改, 他就是 effectively final

    public class OutClass {
    
      public void fn() {
        int a = 123;
    
        class InnerClass {
          public void innerFn() {
            // 内部类 调用 外部的变量, 在 jdk8 之前, a 必须用 final 继续修饰
            // 但是在 jdk8 下, 我们的 a 除了声明式做了赋值, 在整个方法体内没进行其他的修改了
            // 此时 我们的 a 是 effectively final 的, 可以在内部类进行使用
            System.out.println("a : " + a);
          }
        }
        // 如果把下面的 a 赋值 注释去掉, 会报错, a 不是 effectively final 了
        // a = 1233;
      }
    }
    
    
    

    作用

    • 封装性, 隐藏具体的细节, 不让外部类知道
    /**
     * 加密器接口
     */
    public interface Encrypter {
    
        /**
         * 对内容进行加密
         * @param content 加密的内容
         * @return 加密后的内容
         */
        String encrypt(String content);
    }
    
    /**
     * 外部类
     */
    public class OutClass {
    
        /**
         * 隐藏起来的加密器
         */
        private class MyEncrypter implements Encrypter {
            @Override
            public String encrypt(String content) {
                // 在需要加密的内容后面加上 encrypt
                return content + "encrypt";
            }
        }
    
        /**
         * 获取加密器
         * @return
         */
        public Encrypter getEncrypter() {
            return new MyEncrypter();
        }
    }
    
    /**
     * 调用类
     */
    public class Main {
    
        public static void main(String[] args) {
    
            String str = "需要加密的内容";
    
            // 取到加密器, 此处我们只知道通过外部类就能获得加密器,但是加密器的加密过程我们是不知道的
            OutClass outClass = new OutClass();
            Encrypter encrypter = outClass.getEncrypter();
    
            // 加密内容
            String encryptedContent = encrypter.encrypt(str);
    
            System.out.println(encryptedContent);
        }
    }
    
    • 间接实现多重继承(声明多个内部类,每个内部类继承一个类)
    /**
     * 加密器
     */
     public class Encrypter {
    
         /**
          * 对内容进行加密
          * @param content 加密的内容
          * @return 加密后的内容
          */
         public String encrypt(String content) {
             // 在需要加密的内容后面加上 encrypt
             return content + "encrypt";
         }
     }
    
    /**
     * 解密器
     */
     public class Decrypter {
    
        /**
         * 对内容进行解密
         * @param content 需要解密的内容
         * @return 解密后的内容
         */
        public String decrypt(String content) {
    
            // 在需要加密的内容后面加上 encrypt
            int length = "encrypt".length();
            // 解密的内容不为空,同时长度需要大于 length
            if (content == null || "".equals(content) || content.length() < length) {
                return content;
            }
            return content.substring(0, content.length() - length );
        }
    }
    
    
    /**
     * 外部类,此时我们的外部类具备了编码和解码的功能
     */
    public class OutClass {
    
      private class MyEncrypter extends Encrypter{
      }
    
      private class MyDecrypter extends Decrypter{
      }
    
      public String encrypt(String content) {
          return new MyEncrypter().encrypt(content);
      }
    
      public String decrypt(String content) {
          return new MyDecrypter().decrypt(content);
      }
    }
    
    • 方便编写事件驱动程序(如安卓里面的按钮事件等)
    /** 匿名内部类 */
    myBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           //这里写代码
        }
    });
    

    相关文章

      网友评论

          本文标题:[Java基础] 外部类, 内部类

          本文链接:https://www.haomeiwen.com/subject/gnikpctx.html