内部类
- 定义:
定义在一个类里面或者一个方法里面的类,即为内部类。
- 内部类的作用:
1、每个内部类都能独立地继承自某个类或实现某个接口,无论外部类是否继承了某个类或实现某个接口。这个特性使得多重继承成为可能。(例如外部类A中包括继承了B类的内部类C,C类拥有B类的方法,A类拥有C类的方法,所以A类拥有B、C类的方法,相当于多继承)
2、内部类可以很好的实现隐藏,我们控制类的访问权限,是通过类前面的访问修饰符来限制的,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以,所以我们能通过内部类来隐藏我们的信息
3、可以避免修改接口而实现同一个类中两种同名方法的调用。假如一个类要继承一个类,还要实现一个接口,但是要继承的类和接口中由方法名字相同,此时没有办法区分这两个方法,就需要内部类。
- 注意:内部类和外部类之间属性的互相访问,需要通过对象来访问。
- 内部类的种类:
基本上有四种:静态内部类、成员内部类、局部内部类、匿名内部类。
-
1、静态内部类:
定义在类内部的静态类,就是静态内部类。语法如下:
public class Out { public static int age; public String name; public static class Inner { public void sleep(){ } } }
说明:
1、静态内部类可以访问外部类所有的静态变量和方法(包括使用 private 修饰的),但是不能访问外部类的普通变量和方法。和静态方法、静态变量类似,静态内部类在加载的时候就已经创建,不需要依赖外部类的实例,使用静态内部类时,外部类的实例还不一定被声明出来,所以静态内部类不能调用外部类的普通变量和方法。
2、静态内部类可以有自己的静态变量、方法,构造方法等。
3、静态内部类的优点是:外部类加载时并不需要立即加载内部类。其它类使用静态内部类需要使用 “外部类.静态内部类” 方式,如下:
Out.Inner inner = new Out.Inner(); inner.sleep();
-
2、成员内部类
定义在类内部的非静态类,就是成员内部类。语法如下:
public class Outer { private static int age; private String name; public class Inner { public void sleep() { } } }
说明:
1、内部类中不能定义静态成员和方法,因为内部类是外部类的一个成员,只有当外部类初始化时,内部类才能初始化,静态变量属于类级别,在类加载的时候就初始化,所以两者本身在语法上就有矛盾。但可以定义常量,常量是存放在常量池中的,不涉及到类的加载。
2、成员内部类可以访问外部类所有的变量和方法,包括静态和实例,私有和非私有。和静态内部类不同的是,每一个成员内部类的实例都依赖一个外部类的实例。其它类使用内部类必须要先创建一个外部类的实例,创建内部类实例有如下两种方法:方法一(需要在外部类中设置一个方法,用于返回一个内部类对象):
public class Outer { private Inner getInner() { return new Inner(); } public class Inner{} }
//如果你想创建某个内部类对象,必须在 new 表达式中提供其外部类的引用,这时需要使用 .new 语法 Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.sleep();
方法二
Outer outer = new Outer(); Outer.Inner inner = out.getInner() inner.sleep();
成员内部类访问外部类:
成员内部类可以访问外部类的所有成员变量和成员方法(包括private成员和静态成员)。如果要在内部类中生成外部类对象的引用,可以使用 外部类名.this
例如:public class Outer { public class Inner { public Outer getOuter() { return Outer.this; } } public static void main(String[] args) { Outer outer = new Outer(); //Outer.Inner inner = outer.new Inner(); Outer.Inner inner = outer.getInstance(); Outer outer1 = inner.getOuter(); } }
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即内部类中如果调用或者访问同名变量、同名方法,默认访问的是成员内部类的成员。如果在内部类中要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量 外部类.this.成员方法
例如:
package neibulei; /** * Created by joker on 2022/1/5. */ public class Outer { int age =1; String name ="waibulei"; void printName(){ System.out.println("waibulei"); } public Inner getInstance(){ return new Inner(); } public class Inner{ public int age=2; String name ="neibulei"; void printName(){ System.out.println("neibulei"); } void printn(){ System.out.println(age); System.out.println(name); } void printw(){ System.out.println(Outer.this.age); System.out.println(Outer.this.name); } } }
package neibulei; /** * Created by joker on 2022/1/5. */ public class test { public static void main(String[] args) { Outer outer = new Outer(); //Outer.Inner inner= outer.new Inner(); Outer.Inner inner = outer.getInstance(); inner.printn(); inner.printName(); } }
外部类访问成员内部类的属性:
外部类可以访问内部类的所有成员变量和方法(包括private),但外部类想访问成员内部类的成员,必须先创建一个成员内部类的实例,再通过这个实例去点属性、方法来访问。
-
3、局部内部类:
局部内部类是定义在一个方法或代码块里面的类。
语法格式:
public class Outer { public void testFangfa() { class Inner { ... } } }
例如:
package neibulei; /** * Created by joker on 2022/1/5. */ public class juBuNeiBuLei { public void juBuNeiBuLei(){ class ceShi{ public int a =1; public String b ="局部内部类"; public void print(){ System.out.println("局部内部类的方法"); } } } }
注:
1、局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
2、局部内部类不可以定义静态成员和方法。
3、局部类只能在定义该局部类的方法中使用。定义在实例方法中的局部内部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部类还可以访问其所在方法内的常量。 -
4、匿名内部类:
1、定义:没有名字的内部类(包括没有名字的成员内部类和局部内部类),需要使用实例化对象的形式来创建。
2、使用匿名内部类的前提和条件:必须存在继承或实现关系的时候才可以使用。因为匿名内部类没有名字,所以要通过继承父类或者实现接口的形式来创建对象,同时extends和implements也不会显示体现,而是由jvm操作。
3、匿名内部类的对象,通过 new 表达式返回的这个引用被自动向上转型为 父类的引用。
4、匿名内部类没有构造器(因为它没有名字),如果需要为匿名内部类做一些初始化操作,如果该匿名内部类的基类构造器有参数,则可以直接使用,但编译器会要求其参数引用是 final(即使你不加,Java8 也会为我们自动加上 final)。或者通过代码块,模仿构造器的行为来实现初始化。
5、匿名内部类访问外部类属性的权限,需要看这个匿名内部类是成员内部类还是局部内部类,具体来看。语法格式:
//这个 new 开始就是匿名内部类,正常是: 类名 引用名 = new 类名() ,匿名内部类只有 “=” 右边的部分,如下: new 父类名或接口名(){ //匿名内部类的具体实现,重写父类方法或者实现接口的方法。 }; //说明:如果 new 后面的是接口名,此处并不是直接实例化接口,因为java中接口不能实例化,但可以被实现,此处是匿名内部类实现此接口并且自动向上转型,所以这么写没问题。
例如:
package neibulei; /** * Created by joker on 2022/1/6. */ public class niMingNeiBuLei { public int age = 1; public String name = "god"; //匿名局部内部类,此处继承了Object类,此处还可以是实现接口的方式 Object object = new Object(){ int a = 2; String n = "s"; @Override public String toString() { return super.toString(); } }; //匿名成员内部类,此处继承了Object类,此处还可以是实现接口的方式 void run(){ Object object = new Object(){ int b = 3; String m = "x"; @Override public String toString() { return super.toString(); } }; } }
匿名内部类可以作为实参使用。
如何继承内部类?待补充
一个java类中可以有多个 class,但只能有一个 public class。