• 讨论Java中的内部类是什么?


    前言

    内部类,讲完前面的特性,今天就讲下内部类这个用的比较多,出现频率挺高的知识点。

    what is that?

    首先,顾名思义,内部类就是在类的内部,也就是类的类,嵌套在里面的。直接代码介绍,现一般分为成员内部类和局部内部类,还有一种匿名类。内部类拥有对外围对象的引用。大部分使用的都是成员内部类。成员内部类是一种与Field、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。

    成员内部类

    定义在类里面的成员变量域的类,就是成员内部类。此时的内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰。

    class A {
        //成员内部类,这种包含类的结构
    	class b{}
    }
    

    成员内部类还可以分为静态内部类和非静态内部类。注意:根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。

    • 静态内部类(带static)

    static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。静态内部类只可以访问静态的外部类的方法和对象。但是静态内部类可以有非静态成员。

    /**
     * 这一篇是用来测试静态内部类的使用的
     * @author yhy
     * @date 1/14
     */
    public class StaticInner {
        private  int num = 7;
    
        /**
         *  定义静态常量,这个才可以被静态内部类调用
         */
        private  static  int num2 = 6;
        static class Innerclass{
            private static int num3;
            public void innerfangfa(){
    //          System.out.println("调用外部类的成员变量:"+num);,这个会报错,因为不能调用非静态的 
                System.out.println("调用外部类的成员变量:"+num2);
            }
        }
    }
    
    

    报错信息

    外部类使用静态类的代码。

    public class StaticInner {
        private  int num = 7;
    
        /**
         *  定义静态常量,这个才可以被静态内部类调用
         */
        private  static  int num2 = 6;
    
        /**
         * 定义一个静态内部类
         */
        static class Innerclass{
            private static int num3;
            private int num4 = 4;
            public void innerfangfa(){
    //          System.out.println("调用外部类的成员变量:"+num);,这个会报错,因为不能调用非静态的
                System.out.println("调用外部类的成员变量:"+num2);
            }
        }
    
        /**
         * 这是外部类的方法,用来验证访问内部类的方法
         */
        public static void inneracess(){
    //        通过类名访问静态内部类的类成员
            System.out.println("访问静态内部类:"+Innerclass.num3);
    //        通过实例访问静态内部类的实例成员
            System.out.println("访问静态内部类:"+new Innerclass().num4);
    
        }
    }
    
    • 非静态内部类(不带static)

    内部类可以直接访问外部类的变量、方法等,而外部类只能通过显式创建类才可以。包括可以访问外部类的private。同时,在非静态内部类里不能有静态方法、静态field、静态初始化块。

    /**
     * 这一篇是用来讲内部类的一些定义的
     * @author yhy
     * @date 1/14
     */
    public class InnerClass {
        private int outernum = 9;
    
        /**
         * 创建内部类,同时还定义一个常量,为private
         */
        class Inner{
            private  int innernum = 8;
    //        定义内部类的方法,去调用外部类的成员常量
            public void outAcess(){
                System.out.println("外部类的值:"+outernum);
            }
        }
    
        /**
         * 这个是外部类的成员方法
         * 这里直接调用内部类会报错,不可以直接innernum使用
         * 要通过显式创建内部类对象才可以实现
         */
        public void innerAcess(){
            System.out.println("内部类的值:"+new Inner().innernum);
    
        }
        public static void main(String[] args) {
    //        创建外部类对象,没创建内部类对象
         InnerClass i = new InnerClass();
            i.innerAcess();
       
        }
    
    }
    //非静态内部类对象必须寄存在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄存其中
    

    非静态内部类的方法内访问某个变量时,系统优先在该方法内查找(如果存在就使用)——if not,则到该方法所在的内部类中(存在则使用)——if not,则到该内部类所在的外部类(如果存在则使用)——if not,系统将出现编译错误。

    局部内部类

    局部内部类是定义在类的内部方法或者其他作用域里面的内部类。

    class A {
    	//局部内部类
    	public void B(){
    		class B{}
    	}
    }
    

    局部内部类的使用

    package music.daima.ebook;
     /**
     *  * 这一篇是用来观察局部内部类的定义使用等,thinking in java的练习九
     *  * @author yhy
     *  * @date 1/14
     *  */
    
     interface  Ex9Interface{
         /**
          * @param s string类型在下面给内部类调用
          * 定义一个接口,同时包含一个say方法,给下面使用
          */
         void say(String s);
     }
    public class JuBuInner {
         /**
          * @return Inner()返回该类对此接口的引用
          * //    这是一个方法,下面在里面定义一个内部类
          */
    
        Ex9Interface f() {
            class Inner implements Ex9Interface {
                @Override
                public void say(String s) {
                    System.out.println(s);
                }
            }
            return new Inner();
        }
        public static void main(String[] args) {
           JuBuInner x = new JuBuInner();
    //       调用局部内部类的say方法
            x.f().say("hi");
        }
    }
    
    
    • 局部内部类不可以为publicprivate来修饰。而且因为它的上一级是方法,所以用static来修饰它是没有意义的,所以也不能用static来修饰

    匿名内部类

    这个知识点就直接看代码,是内部类的简化写版 ,本质不是类而是匿名对象(继承该类或者实现了改接口的)。

    前提

    得提前存在一个类或者是接口给它用,可以是具体类也可以是抽象类。

    格式

    new 类名or接口名() {重写方法};

    package music.daima.ebook;
    /**
     * thinking in java
     * @author yhy
     * 匿名内部类
     */
    public class AnonymousInner {
        public static test1 getTest(int i){
    //        匿名类的结构如下,这里的i会传给基类的构造器
            return new test1(i) {
                {
                    System.out.println(22);
                }
    //            重写f方法
                @Override
                public void f() {
                    System.out.println("在匿名类内");
                }
            };//这里要注意有分号
        }
        public static void main(String[] args) {
            test1 abc = getTest(55);
            abc.f();
        }
    }
    
    /**
     *创建一个抽象类
     */
    abstract class test1{
    //    基类的构造器
        public test1(int i){
            System.out.println("i is :"+i);
        }
        public abstract void f();
    }
    

    《Java的编程思想》中的练习题答案——使用匿名内部类

    interface Inner12 {
    	void modifyOuter();
    }
    
    public class Outer12 {
    	private int oi = 1;
    	private void hi() { System.out.println("Outer hi"); }
    	public Inner12 inner() {
    		return new Inner12() {
    			public void modifyOuter() { 
    				oi *= 2;
    				hi(); 
    			}
    		}; 
    	}
    	public void showOi() { System.out.println(oi); }
    	public static void main(String[] args) {
    		Outer12 out = new Outer12();
    		out.showOi();
    		out.inner().modifyOuter();
    		out.showOi();
    	}
    }
    

    下面的pd.method()方法调用结果是一样的,下面用的就是匿名内部类,相对简单一点,但是不好理解。记住它是一个对象而不是类。

    why use it?

    1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据

    2. 内部类可以对同一个包内的其他类隐藏起来

    3. 当想要定义一个回调函数且不想编写大量代码,可以考虑使用匿名内部类比较便捷

      ​ ——《Java核心技术卷一》

    个人理解:

    • 内部类可以将其封装起来,很好的隐藏,可以用protected 和private权限来修饰。
    • 拥有访问外部类的所有元素的权限,普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。
    • 比如可以很好处理类的办法同名重叠情况,更加有效地去引用。
    • 可以间接实现了多重继承,内部类继承其它类,然后实例化外部类可以实现这种间接效果

    how to use?

    1.在外部类以外使用静态内部类

    (因为静态内部类是外部类类相关的,因此创建内部类对象时无须创建外部类对象。)

    /**这是用来测试在外部类以外使用静态内部类
     * @author yhy
     */
    public class innerOut {
        public static void main(String[] args) {
            Out1.In1 abc = new Out1.In1();
        }
    }
    
    /**
     * 定义一个外部类
     * @author yhy
     */
    class Out1{
        /**
         * 定义一个静态内部类,静态内部类是直接与外部类相关的
         */
        static class In1{
            void FangFa(){
                System.out.println("这是静态内部类");
            }
        }
    }
    //output:这是静态内部类
    
    

    2.在外部类以外使用非静态内部类

    /**
     * 使用教程——外部类以外使用非静态内部类
     * @author yhy
     */
    public class InnerTest {
        public static void main(String[] args) {
    //        格式,先外部再内部 = new 外部.new 内部
            Out.In test = new Out().new In();
            test.inner();
            
        }
    }
    /**
     * 定义一个外部类
     */
    class Out{
        /**
         * 定义一个非静态内部类
         */
        class In{
            void inner(){
                System.out.println("打印出内部类的信息");
            }
        }
    }
    //output:打印出内部类的信息
    

    3.在外部类内部使用内部类

    在上面有使用的代码

    总结

    静态内部类和非静态内部类区别只是在创建内部类对象时,静态的只需使用外部类即可调用构造器,而非静态则一定使用外部类对象来调用里面的方法。

  • 相关阅读:
    关于json中对象的删除
    JDBC操作数据库 封装好的工具类
    json <--->List集合,实体类 之间的相互转换
    java--->>发送邮件
    登陆的过滤器
    Hadoop + Spark 在CentOS下的伪分布式部署
    CentOS和ubuntu修改hostname的区别
    ubuntu 用户管理 adduser vs useradd
    hadoop2.6.1源码编译64位
    MySQL Binlog详解
  • 原文地址:https://www.cnblogs.com/yhycoder/p/12191697.html
Copyright © 2020-2023  润新知