Java内部类
什么是内部类?
- 类的五大成员:属性、方法、构造方法、代码块、内部类
- 在一个类的里面,再定义一个类
public class Outer { // 外部类class Inner { // 内部类}
}
public class Test { // 外部其他类public static void main(String[] args) {}
}
-
内部类表示的事物是外部类的一部分
-
内部类单独出现没有任何意义
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
内部类的分类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
1、成员内部类
- 写在成员位置的,属于外部类的成员
- 成员内部类可以被一些修饰符所修饰,比如:
private
,默认,protected
,public
,static
等 - 在成员内部的类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量
1.1获取成员内部类对象的两种方法:
- 在外部类的编写方法,对外提供内部类的对象
- 直接创建格式:
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
示例:
public class Outer { // 外部类String name;int a;class Inner { // 内部类int a = 10;}private class Iner { // (私密)内部类int a = 10;public void sj() {System.out.println("我是私密的内部类");}}public Iner getIner() { // 获取私密内部类的方法return new Iner();}public void getmessageIner() { // 获取私密内部类中内容的方法Iner in = getIner();System.out.println(in.a); // 输出: 10in.sj(); // 输出: 我是私密的内部类}
}
package com.inner.a01innerclassdemo01;public class Test { // 测试类public static void main(String[] args) {// 默认访问修饰符 直接创建内部类Outer.Inner oi = new Outer().new Inner();System.out.println(oi.a); // 输出: 10// 如果加上私有的private修饰符// 要么改变访问修饰符,要么在外部类编写方法,对外提供内部类对象Outer o = new Outer();Object iner = o.getIner();// 但也只能通过它的父类Object获取它(多态),但并不能使用它(因为父类是Object)// 如果要想使用它内部的成员变量和方法,只能在外部类中创建公共方法o.getmessageIner();}
}
1.2当外部类变量重名时:
外部类成员变量和内部类成员变量重名时,使用外部类名.this.变量名
public class Outer {private int a = 10;class Inner {private int a = 20;public void show() {int a = 30;System.out.println(Outer.this.a); // 输出: 10System.out.println(this.a); // 输出: 20System.out.println(a); // 输出: 30}}
}
1.3成员内部类的内存图:
- 测试类的字节码文件加载到内存,虚拟机会自动调用
main
方法,main
方法会加载进栈 - 第一行代码,发现用到了
Outer
和Inner
,所以就会把外部类和内部类的字节码文件加载到方法区(两个独立的字节码文件)。等号左边,在栈区开辟一个空间用来记录Inner
对象的地址值;等号右边,分为两个部分,第一部分new Outer()
会在堆区开辟一个新空间存放外部类对象,第二部分new Inner()
会在堆区再次开辟一个空间存放内部类对象,同时Java
还会给内部类的对象,去加一个隐藏的成员变量this
——用在记录外部类成员对象的地址值,并将内部类对象的地址值返回给栈区的oi
变量 - 第二行代码,调用
oi
的show
方法,此时show
方法会被加载建栈,此时的调用者的地址值是002
,第一条输出语句,就近原则;第二条输出语句,打印调用者里面的变量;第三条输出语句,有Outer
前缀,会先从内部类对象中找到外部类对象的地址001
,在打印外部类里面的变量
2、静态内部类
静态内部类是一种特殊的成员内部类。
静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象。
public class Outer { // 外部类static class Inner { // 静态内部类}
}
2.1创建静态内部类的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
- 调用非静态方法的格式:先创建对象,用对象调用
- 调用静态方法的格式:
外部类名.内部类名.方法名();
示例:
public class Outer { // 外部类// 静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建对象int a = 10;static int b = 20;// 静态内部类static class Inner {public void show01() {// 会报错
// System.out.println(a);// 若想要访问外部类中的对象 则需要创建对象Outer o = new Outer();System.out.println(o.a); // 输出: 10System.out.println(b); // 输出: 20System.out.println("非静态的方法被调用了");}public static void show02() {// 报错
// System.out.println(a);Outer o = new Outer();System.out.println(o.a); // 输出: 10System.out.println(b); // 输出: 20System.out.println("静态的方法被调用了");}}
}
public class Test {public static void main(String[] args) {// 创建静态内部类的对象// 只要是静态的东西,都可以用类名.直接调用Outer.Inner oi = new Outer.Inner();oi.show01();// 若想要调用静态方法oi.show02();// 这种方法可以,但不提倡// 可以通过类名直接调用静态方法Outer.Inner.show02();}
}
3、局部内部类
- 将内部类的定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。(不能使用四大权限修饰符,可以使用
final
等) - 外界是无法直接使用,需要在方法内部创建对象并使用。
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
示例:
public class Outer { // 外部类int a = 10;public void show() {int b = 20;// 局部内部类class Inner {String name;// 该类可以直接访问外部类的成员int age = a;public void method01() {System.out.println(a); // 输出: 10System.out.println(b); // 输出: 20System.out.println("局部内部类中的method01方法");}public static void method02() {System.out.println("局部内部类中的method02方法");}}// 要想访问,必须在方法内部创建对象并使用Inner i = new Inner();System.out.println(i.name); // 输出: nullSystem.out.println(i.age); // 输出: 10i.method01();Inner.method02();}
}
public class Test { // 测试类public static void main(String[] args) {Outer o = new Outer();o.show();}
}
4、匿名内部类
-
匿名内部类的本质上就是隐藏了名字的内部类。(只是自己没写名字,编译java会自己生成相应的
class
文件——Test$1.class
数字依次累加) -
隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置。
4.1使用场景:
- 当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类对象只要使用一次,就可以用匿名内部类简化代码。
4.2匿名内部类的格式:
new 类名或者接口名() {重写方法;
};
4.3格式的细节:
- 包含了继承或实现,方法重写,创建对象。整体就是一个类的子类对象或者接口的实现对象
示例:
public interface Swim { // Swim接口public abstract void swim();
}
public abstract class Animal { // Animal抽象类public abstract void show();
}
public class Test { // 测试类public static void main(String[] args) {use(new Swim() {@Overridepublic void swim() {System.out.println("打印重写的游泳接口");}});use(new Animal() {@Overridepublic void show() {System.out.println("打印重写的动物类");}});// 也可以直接使用new Animal() {@Overridepublic void show() {System.out.println("重写了动物这个类中的方法");}}.show(); // 重写了动物这个类中的方法Swim s = new Swim() {@Overridepublic void swim() {System.out.println("重写了游泳的接口");}};s.swim(); // 重写了游泳的接口}// 使用匿名内部类public static void use(Swim s) {// 相当于Swim s = new Swim(){};s.swim(); // 打印重写的游泳接口}public static void use(Animal a) {// 相当于Animal a = new Animal(){};a.show(); // 打印重写的动物类}
}