搜尋
首頁Javajava教程Java內部類別的使用方法

    一. 內部類別簡介

    1. 概念

    在Java中,我們通常是把不同的類別創建在不同的包裡面,對於同一個包裡的類別來說,它們都是同一層次的。但其實還有另一種情況,有些類可以被定義在另一個類的內部,我們把在一個類裡面定義的類稱為內部類(InnerClass)或嵌套類,把外面定義的類稱為外部類別(OutClass)或宿主類別。  也就是說,在類別的內部既可以定義成員變數和方法,也可以定義其他的類別。定義內部類別的常見格式如下:

    class Outer {//外部类
        
        class Inner {//内部类
            
            //方法和属性
        }
        
    }

    上面的程式碼中,Outer是普通的外部類,Inner就是內部類別。 它與普通外部類別最大的不同,在於其實例物件不能單獨存在,必須依附於一個外部類別的實例物件。

    內部類別可以很好地實現隱藏,一般的非內部類別是不允許有private 與protected權限的,但內部類別卻可以,而且內部類別還擁有外部類別中所有元素的存取權限。總之,對內部類別的許多存取規則都可以參考變數和方法。

    但是要注意,雖然我們使用內部類別可以使程式結構變得更加緊湊,但卻在一定程度上破壞了物件導向的想法。

    2. 優點

    內部類別的存在,具有以下優點:

    • 內部類別使得多重繼承的解決方案變得更完整:每個內部類別都能獨立的實作接口,無論外部類別是否已經實現了接口或繼承了父類,對於內部類別都沒有影響;

    • 既可以方便地將存在一定邏輯關係的類別組織在一起,又可以對外界隱藏;

    • #方便各類編寫事件驅動程式;

    • #方便編寫線程程式碼。

    3. 分類

    Java中的內部類別可以分為以下幾種類型:

    • 成員內部類別

    • 靜態內部類別

    • 局部內部類別

    • #匿名內部類別

    #雖然大多數時候,內部類別用得不多,但我們也有必要了解它們是如何具體使用的。

    4. 內部類別的特點

    內部類別相比外部類,具有以下特點:

    • 內部類別可以存取外部類別的私有成員,且不破壞封裝性;

    • 內部類別仍是一個獨立的類,在編譯之後內部類別會被編譯成獨立的.class文件,但前面會冠以外部類的類別名稱和$符號,該檔案名稱的格式是外部類別名稱$內部類別名稱.class;

    • 因為內部類別是外部類別的一個成員,所以內部類別不能用普通的方式訪問,但內部類可以自由地訪問外部類裡的成員變量,無論是否被private修飾;

    • 如果是靜態內部類,我們不能隨便訪問外部類的成員變量,只能存取外部類別的靜態成員變數。

    5. Java類別的建立需求

    我們在建立定義Java類別時,應該遵循以下要求:

    • 一個java檔案中可以寫多個類,但只能有一個類別使用public關鍵字進行修飾,這稱之為主類;

    • 主類名必須與檔名一致,在開發中,應盡量只在一個java檔中寫一個類別;

    • 外部類別只有兩種存取等級:public 和預設;內部類別則有4 個存取等級: public、protected、 private 和預設;

    • 在外部類別中,可以直接透過內部類別的類別名稱存取內部類別;

    • 在外部類別以外的其他類別中,需要透過內部類別的完整類別名稱來存取內部類別;

    • 內部類別與外部類別不能重新命名。

    接下來我們就針對上面提到的幾種內部類,分別告訴大家這幾種內部類別的用法。

    二.成員內部類別

    1. 概念

    成員內部類別就是指沒有被static修飾的內部類,也可以稱為非靜態內部類。

    2. 特點

    成員內部類別具有以下特點:

    • 在早期的jdk版本中,成員內部類別中只能定義非靜態的屬性和方法,除非同時使用final和static進行修飾;

    • 在新版的jdk中,成員內部類別中也可以定義靜態的屬性和方法;

    • 成員內部類別可以存取外部類別的所有成員,包括私有和靜態的成員,即使是多層嵌套時也是如此;

    • #外部類別不能直接存取內部類別的成員,必須透過內部類別的實例物件存取;

    • 在外部類別的靜態方法和外部類別以外的其他類中,必須透過外部類別的實例建立內部類別的實例物件;

    • 外部类的实例与内部类实例是一对多的关系,即一个内部类实例只对应一个外部类实例,但一个外部类实例则可以对应多个内部类实例。

    3. 语法

    如果是在外部类中,创建成员内部类对象的基本语法格式如下:

    内部类 对象名 = new 内部类();

    如果是在外部的其他类中,或者是在外部类的静态方法中,创建成员内部类对象的基本语法格式如下:

    内部类 对象名 = new 外部类().new 内部类();

    4. 案例

    4.1 定义成员内部类

    /**
     * 成员内部类
     */
    public class OuterClass {
    
    	// 外部类的非静态成员
    	String name = "一一哥";
    	private String hobby = "撸码";
    	static int age = 30;
    
    	// 非静态方法
    	public void show() {
    		//这里的this是指OuterClass对象
    		System.out.println("show方法...name="+this.name);
    
            //如果是在外部类里面创建内部类的对象,就不需要创建外部类实例,可以直接new 内部类()
    		//InnerClass inner = new InnerClass();
    	}
    
    	// 定义一个成员内部类
    	public class InnerClass {
    		// 也可以定义私有属性
    		private int a = 10;
    
    		//在早期的JDK中,成员内部类中不能定义静态变量;但在新版JDK中,成员内部类中可以定义静态变量
    		static int b = 20;
    
    		// 非静态方法
    		public void m1() {
    			// 这里的this对象是InnerClass内部类对象
    			System.out.println("成员内部类的成员变量:" + this.a);
    			
    			//外部类.this.属性或方法,这个this是外部类对象
    			System.out.println("外部类的成员变量:" + OuterClass.this.name);
    			
                //内部类中可以访问外部类的私有成员和静态成员
    			System.out.println("外部类的私有成员变量:" + hobby);
    			System.out.println("外部类的静态变量:" + age);
    		}
    
    		//在早期的JDK中,成员内部类中不能定义静态方法;但在新版JDK中,成员内部类中可以定义静态方法
    		public static void m2() {
    			System.out.println("调用成员内部类的静态变量:" + b);
    			System.out.println("调用外部类的静态变量:" + age);
    
                //在静态方法中创建内部类对象,也要通过内部类 对象名 = new 外部类().new 内部类();的格式
    			//InnerClass innerClass = new OuterClass().new InnerClass();
    		}
    
    	}
    
    }

    我们要注意,在早期的JDK中,成员内部类中不能定义静态属性和方法;但在新版JDK中,成员内部类中可以定义静态的属性和方法。并且我们要搞清楚在不同的位置上,创建内部类对象的方式,以及this的具体含义。

    4.2 定义测试类

    我们在外部的其他类中,要想创建出一个成员内部类的对象,需要通过如下形式:

    内部类 对象名 = new 外部类().new 内部类();

    public class InnerClassTest {
    
    	public static void main(String[] args) {
    		
    		//在外部的其他类中,不能直接创建内部类对象,否则:
    		//No enclosing instance of type OuterClass is accessible. 
    		//Must qualify the allocation with an enclosing instance of type OuterClass 
    		//(e.g. x.new A() where x is an instance of OuterClass).
    		//InnerClass inner=new InnerClass();
    
            //在外部的其他类中创建内部类对象,需要通过如下格式:
    		//内部类 对象名 = new 外部类().new 内部类();
    		//InnerClass inner=new OuterClass().new InnerClass();
    		
    		//也可以拆分成如下格式:
    		OuterClass outer=new OuterClass();
    		InnerClass inner=outer.new InnerClass();
    		
    		inner.m1();
    		
    		InnerClass.m2();
    		
    	}
    }

    5. 访问方式小结

    学习到这里,你可能会被内部类与外部类之间的调用访问关系整蒙圈,所以给大家梳理了一下访问方式:

    • 成员内部类 访问 外部类的成员(属性、方法),可以【直接访问使用】;

    • 外部类 访问 成员内部类,需要【直接创建内部类对象后再访问】,即 new InnerClass();

    • 外部的其他类 访问 成员内部类,需要【创建外部类对象,再创建内部类对象后访问】,即 InnerClass inner=new OuterClass().new InnerClass();

    6. 关于this的注意事项

    在之前给大家讲过this的作用和用法,但在内部类中,关于this,我们需要注意以下两点:

    如果同时存在外部类和内部类,那么this在哪个类中使用,this就代表哪个类的对象;

     如果内部类想要通过this来调用外部类的属性和方法,需要使用外部类名.this.属性或者方法名。

    三. 局部内部类

    1. 概念

    局部内部类是指在方法中定义的内部类。

    2. 特点

    局部内部类具有如下特点:

    • 局部内部类只能在方法中定义和创建对象,也只在当前方法中有效;

    • 局部内部类中可以访问外部类的所有成员;

    • 局部内部类与局部变量一样,不能使用访问控制修饰符(public、private和protected)和static修饰符;

    • 在jdk 7版本中,如果局部变量是在局部内部类中使用,必须显式地加上final关键字;在jdk 8版本中,会默认添加final关键字;

    • 局部内部类只能访问当前方法中final类型的参数与变量。如果方法中的成员与外部类的成员同名,可以使用 .this. 的形式访问外部类成员;

    • 局部内部类中还可以包含内部类,但这些内部类也不能使用访问控制修饰符(public、private 和 protected) 和 static修饰符;

    • 局部变量在方法执行结束后会被销毁,而局部内部类的对象会等到内存回收机制进行销毁。如果是局部内部类里的常量,该常量会被存放在常量池中。

    3. 语法

    创建局部内部类对象的基本语法格式如下:

    public class PartClass {
        
        public void method() {
            //在方法中定义的内部类,就是局部内部类
            class Inner {
                //属性
    
                //方法
            }
        }
        
    }

    4. 案例

    4.1 定义局部内部类

    我们来定义一个局部内部类的案例代码。

    /**
     * 
      * 局部内部类---定义在方法中的内部类
     */
    public class PartOuterClass {
    
    	//类的成员变量
    	String name="一一哥";
        private int age=30;
        static String hobby="java";
        
        public void show() {
        	//局部变量
            //JDK 7之前,匿名内部类和局部内部类中访问外部的局部变量时,该变量需要明确地带有final修饰符
            //final int num = 10;
            
            //Effectively final特性
            int num = 10;
            
            //局部内部类,类似于是方法中的局部对象
            class PartInnerClass{
            	
            	//内部可以正常定义方法
                public void m1() {
                	//访问外部类的非静态成员,可以使用OuterClass.this.成员的格式,也可以直接访问
                	//System.out.println("外部类的成员变量"+name);
                    System.out.println("外部类的成员变量"+PartOuterClass.this.name);
                    System.out.println("外部类私有的成员变量"+age);
                    System.out.println("外部类的静态变量"+hobby);
                    
                    //局部内部类,可以直接访问方法中的局部变量
                    System.out.println("访问局部变量"+num);
                }
                
                //在新版的jdk中,也可以定义静态的属性和方法,老版的jdk则不行
                static int b=10;
                
                public static void m2() {
                	System.out.println("外部类的静态变量,hobby="+hobby+",b="+b);
                }
            }
            
            //创建局部内部类对象
            PartInnerClass inner = new PartInnerClass();
            inner.m1();
            
            //在当前类中,局部内部类可以直接访问静态成员
            PartInnerClass.m2();
            
        }    
        
    }

    在JDK 7之前,匿名内部类和局部内部类中访问外部的局部变量时,该变量需要明确地带有final修饰符。但从JDK 8之后,我们可以不带final修饰符,而是由系统默认添加了。

    4.2 定义测试类

    接下来我们对上面的案例进行测试。

    public class PartInnerClassTest {
    
    	public static void main(String[] args) {
    		//创建外部类对象,调用方法,执行局部内部类
    		PartOuterClass outer=new PartOuterClass();
    		outer.show();
    	}
    }

    4.3 Effectively final特性

    一般情况下,Java中的局部内部类和匿名内部类访问局部变量时,该变量必须由 final修饰,以保证内部类和外部类的数据一致性。但从 Java 8开始,我们可以不加 final修饰符,而是由系统默认添加,当然这在 Java 8以前是不允许的。Java将这个新的特性称为 Effectively(有效的、实际的) final 功能

    另外在 Lambda表达式中,使用局部变量时也要求该变量必须是 final 修饰的,所以 effectively final特性在 Lambda表达式的上下文中非常有用。

    其实effectively final特性,只是让我们不用显式地把变量声明为final修饰的,它给我们自动添加了final修饰词,但并没有取消final,主要是减少了一点不必要的操作,给开发节省了点时间。

    四. 匿名内部类

    1. 概念

    匿名内部类就是指没有类名的内部类,必须在创建时使用 new 语句来声明。匿名内部类不能在Outer Class外部类中定义,而是要在某个方法的内部,通过匿名类(Anonymous Class)的形式来定义。 匿名内部类本身就是一个对象。

    通常情况下,如果一个方法的参数是接口类型,且该接口只需要实现一次,那么我们就可以通过匿名内部类的形式来进行定义。另外如果该接口的实现每次都不同,也可以使用匿名内部类的形式进行定义。我们也可以把这种定义形式叫做 “接口回调” 。匿名内部类的代码格式使得代码更加简洁、紧凑,模块化程度也更高。

    2. 特点

    匿名内部类具有如下特点:

    • 匿名内部类本身就是一个对象;

    • 一般在匿名内部类中不会定义属性和方法,因为没有意义;

    • 匿名内部类的父类一般都是抽象类或者是接口;

    • 匿名内部类和局部内部类一样,可以访问外部类的所有成员;

    • 如果匿名内部类位于方法中,则该类只能访问方法中 final 类型的局部变量和参数;

    • 匿名内部类中允许使用非静态代码块对成员进行初始化操作;

    • 匿名内部类的非静态代码块会在父类的构造方法之后被执行。

    3. 语法

    通常匿名内部类有两种实现方式:

    • 继承一个类,重写其方法;

    • 实现一个或多个接口,并实现其方法。

    创建匿名内部类对象的基本语法格式如下:

    new  (){
    重写类或接口的方法
    }

    4. 案例

    为了给大家演示匿名内部类的用法,接下来壹哥设计一个用于模拟按钮点击事件的案例。当我们进行安卓等设备开发时,面板上有个按钮,点击该按钮,如何监听点击事件?在Android系统中提供了各种对应的按钮点击监听事件。所以这里壹哥就通过实现接口的形式来定义匿名内部类,模拟一个单击事件。

    4.1 定义接口

    首先我们需要定义一个接口,表示单击监听,内部有个点击事件。

    /**
     * 点击监听事件
     */
    public interface OnClickListener {
    
    	//点击事件
    	void onClick();
    	
    }

    4.2 定义Button按钮类

    然后定义一个Button按钮类,给Button按钮安排一个点击监听方法。

    /**
     * 
     * 局部内部类---定义在方法中的内部类
     */
    public class Button {
    
    	//处理案例点击的监听事件
        public void setOnClickListener(OnClickListener listener) {
        	
        	listener.onClick();
        }
        
    }

    4.3 定义测试类

    接下来我们就测试运行上面的代码。

    /**
     * 匿名内部类测试
     */
    public class AnonyInnerClassTest {
    
    	public static void main(String[] args) {
    		//外部变量
    		int num=20;
    		
    		//测试匿名内部类
    		Button btn=new Button();
    		
    		//模拟处理按钮的点击事件
    		btn.setOnClickListener(new OnClickListener() {//这里就是一个匿名内部类
    			
    			//在匿名内部类中,可以允许使用非静态代码块进行成员初始化操作。
    			int i; 
    			
    			{    // 非静态代码块,在构造方法之后执行
    		        i = 100;    //成员初始化
    		    }
    			
    			@Override
    			public void onClick() {
    				System.out.println("按钮被点击啦...i="+i+",num="+num);
    			}
    		});
    		
    	}
    }

    根据上面的案例可知:

    • 在匿名内部类中,可以允许使用非静态代码块进行成员初始化操作;

    • 匿名内部类的非静态代码块,会在构造方法之后执行;

    • 匿名内部类也可以直接使用外部类的成员。

    五. 静态内部类

    1. 概念

    静态内部类和成员内部类的定义类似,但要使用static修饰,所以称为静态内部类(Static Nested Class)。

    静态内部类和成员内部类有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this的方式调用。但它可以访问Outer类的private静态字段和静态方法,如果我们把静态内部类移到Outer类之外,就失去了访问private的权限。

    2. 特点

    静态内部类中可以定义非静态的属性和方法,也可以定义静态的属性和方法;

    静态内部类中只能访问静态外部类的静态属性和方法。

    3. 语法

    创建静态内部类对象的基本语法格式如下:

    内部类 对象名 = new 外部类.内部类();

    4. 案例

    4.1 定义静态内部类

    这里我们先简单定义一个静态内部类,后面我们在学习内部类时再专门讲解。在这个静态内部类中,定义了一个方法,来访问外部类中的普通属性和静态属性。我们要记住以下几点:

    • 静态内部类访问外部类的成员变量时,需要先创建外部类对象;

    • 非静态内部类可以直接访问使用外部类的成员变量,如同使用本类中的变量;

    • 所有的内部类访问外部类的静态变量时,可以直接通过"外部类.静态变量"的形式访问。

    /**
     * 外部类和内部类
     */
    public class OuterClass {
    	
    	//普通属性,属于外部类
    	static int outerNum=10;
    	
    	//定义一个静态的内部类,如果不带static,就是一个普通的内部类。
    	//内部类的使用,和普通类一样,里面可以正常定义属性、方法、构造方法等。
        //static前面可以带public等任意访问修饰符,也可以不带!
    	static class InnerClass{
    		//私有属性无法在类的外部直接访问
    		//private int innerNum=20;
    		
    		int innerNum=20;
    		
    		public void printNum() {
    			//定义外部类对象
    			OuterClass outer=new OuterClass();
    
                //这里的this是指InnerClass内部类对象!
    			System.out.println("innerNum="+this.innerNum+",outerAge="+outer.outerAge+",outerNum="+OuterClass.outerNum);
    		}
    	}
    
    }

    对于静态内部类而言,static前面可以带public等任意访问修饰符,也可以不带!

    4.2 定义测试类

    我们再定义一个测试类,看看内部类对象是怎么调用的。

    /**
     * 测试访问内部类
     */
    public class InnerClassTest {
    	
    	public static void main(String[] args) {
    		//创建内部类对象,格式为“外部类.内部类 对象名 = new 外部类.内部类的构造方法”
    		OuterClass.InnerClass inner = new OuterClass.InnerClass();
    		
    		//调用内部类的方法
    		inner.printNum();
    		
    		//访问外部类属性
    	    System.out.println("outerNum="+OuterClass.outerNum);
    	    
    	    //访问内部类属性
    	    System.out.println("innerNum="+inner.innerNum);
    	}
    
    }

    5. 访问方式小结

    对于静态内部类的访问要求,给大家总结如下:

    • 静态内部类中可以直接访问外部类的所有静态方法,包含私有的,但不能直接访问非静态成员;

    • 静态内部类可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员;

    • 如果静态内部类 访问 外部类 的静态属性、静态方法等,访问方式是【直接访问】;

    • 如果外部类或外部的其他类来 访问 静态内部类,访问方式是【外部类.内部类 对象名 = new 外部类.内部类的构造方法】,创建出内部类对象后再访问

    以上是Java內部類別的使用方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述
    本文轉載於:亿速云。如有侵權,請聯絡admin@php.cn刪除
    带你搞懂Java结构化数据处理开源库SPL带你搞懂Java结构化数据处理开源库SPLMay 24, 2022 pm 01:34 PM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于结构化数据处理开源库SPL的相关问题,下面就一起来看一下java下理想的结构化数据处理类库,希望对大家有帮助。

    Java集合框架之PriorityQueue优先级队列Java集合框架之PriorityQueue优先级队列Jun 09, 2022 am 11:47 AM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于PriorityQueue优先级队列的相关知识,Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,下面一起来看一下,希望对大家有帮助。

    完全掌握Java锁(图文解析)完全掌握Java锁(图文解析)Jun 14, 2022 am 11:47 AM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于java锁的相关问题,包括了独占锁、悲观锁、乐观锁、共享锁等等内容,下面一起来看一下,希望对大家有帮助。

    一起聊聊Java多线程之线程安全问题一起聊聊Java多线程之线程安全问题Apr 21, 2022 pm 06:17 PM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于多线程的相关问题,包括了线程安装、线程加锁与线程不安全的原因、线程安全的标准类等等内容,希望对大家有帮助。

    Java基础归纳之枚举Java基础归纳之枚举May 26, 2022 am 11:50 AM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于枚举的相关问题,包括了枚举的基本操作、集合类对枚举的支持等等内容,下面一起来看一下,希望对大家有帮助。

    详细解析Java的this和super关键字详细解析Java的this和super关键字Apr 30, 2022 am 09:00 AM

    本篇文章给大家带来了关于Java的相关知识,其中主要介绍了关于关键字中this和super的相关问题,以及他们的一些区别,下面一起来看一下,希望对大家有帮助。

    Java数据结构之AVL树详解Java数据结构之AVL树详解Jun 01, 2022 am 11:39 AM

    本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于平衡二叉树(AVL树)的相关知识,AVL树本质上是带了平衡功能的二叉查找树,下面一起来看一下,希望对大家有帮助。

    java中封装是什么java中封装是什么May 16, 2019 pm 06:08 PM

    封装是一种信息隐藏技术,是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法;封装可以被认为是一个保护屏障,防止指定类的代码和数据被外部类定义的代码随机访问。封装可以通过关键字private,protected和public实现。

    See all articles

    熱AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智慧驅動的應用程序,用於創建逼真的裸體照片

    AI Clothes Remover

    AI Clothes Remover

    用於從照片中去除衣服的線上人工智慧工具。

    Undress AI Tool

    Undress AI Tool

    免費脫衣圖片

    Clothoff.io

    Clothoff.io

    AI脫衣器

    AI Hentai Generator

    AI Hentai Generator

    免費產生 AI 無盡。

    熱門文章

    R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
    3 週前By尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.最佳圖形設置
    3 週前By尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.如果您聽不到任何人,如何修復音頻
    3 週前By尊渡假赌尊渡假赌尊渡假赌

    熱工具

    SublimeText3 英文版

    SublimeText3 英文版

    推薦:為Win版本,支援程式碼提示!

    禪工作室 13.0.1

    禪工作室 13.0.1

    強大的PHP整合開發環境

    Atom編輯器mac版下載

    Atom編輯器mac版下載

    最受歡迎的的開源編輯器

    MinGW - Minimalist GNU for Windows

    MinGW - Minimalist GNU for Windows

    這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

    Dreamweaver Mac版

    Dreamweaver Mac版

    視覺化網頁開發工具