java中内部类
一、内部类
内部类分为四种:成员内部类、静态内部类、局部(方法)内部类、匿名内部类。
public class Outer{
class Inner{
//内部类
}
}
我们在这里先简单介绍一下各个内部类的特点
-
非静态内部类(成员内部类)
作为类的成员,存在于某个类的内部。
成员内部类可以调用外部类的所有成员,但只有在创建了外部类的对象后,才能调用外部的成员, 外部类如果需要访问非静态内部类的实例成员,必须先创建非静态内部类。
如果需要访问非静态内部类的话,必须先创建外部类。 -
静态内部类
作为类的静态成员,存在于某个类的内部。
静态内部类虽然是外部类的成员,但是在未创建外部类的对象的情况下,可以直接创建静态内部类的对象。静态内部类可以引用外部类的静态成员变量和静态方法,但不能引用外部类的普通成员。 -
局部内部类
存在于某个方法的内部。
局部内部类只能在方法内部中使用,一旦方法执行完毕,局部内部类就会从内存中删除。
必须注意:如果局部内部类中要使用他所在方法中的局部变量,那么就需要将这个局部变量定义为final的。 -
匿名内部类
存在于某个类的内部,但是无类名的类。
匿名内部类的定义与对象的创建合并在一起,匿名内部类一般通过如下形式定义,并且在定义的同时进行对象的实例化。
new 类或者接口的名字(){
匿名内部类的主体,大括号中是匿名内部类的主体,这个主体就是类或者接口的实现,如果是类,那么匿名内部类是该类的子类,如果是接口,匿名内部类需要完成接口的实现。
}
二、为什么要使用内部类
- 每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
- 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
- 内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问。
- 创建内部类对象的时刻并不依赖于外围类对象的创建。
三、如何使用内部类
图片转自Java新生代。
(一)非静态内部类(成员内部类)
public class Outer {
private String outerVariable = "外部类的实例变量";
private String commonVariable = "同名变量(外部类)";
private static String outerStaticVariable = "外部静态(类)的类变量";
public void outerMethod() {
System.out.println("我是外部类的实例方法");
}
public static void outerStaticMethod() {
System.out.println("我是外部类的静态方法");
}
/**
* 内部类
*/
public class Inner {
private String commonVariable = "同名变量(内部类)";
public Inner() {
System.out.println("内部类的构造器");
}
public void innerShow() {
//当和外部类冲突时,直接引用属性名,是内部类的成员属性
System.out.println( commonVariable);
//内部类访问外部属性
System.out.println( outerVariable);
System.out.println(outerStaticVariable);
//当和外部类属性名重叠时,可通过外部类名.this.属性名
System.out.println(Outer.this.commonVariable);
//访问外部类的方法
outerMethod();
outerStaticMethod();
}
}
/**
* 外部类访问内部类信息
*/
public void test(){
Inner in=new Inner();
in.innerShow();
}
}
---------------------------------------------------------------------
其他类使用成员内部类
public class Other {
public static void main(String[] args) {
//创建外部类实例
Outer outer = new Outer();
//通过外部类实例和new来调用内部类构造器创建非静态内部类实例
Outer.Inner in;
in = Outer.new in();
//可总结为
Outer.Inner in = new Outer().new Inner();
}
输出为:
外部类构造器
内部类的构造器
同名变量(内部类)
外部类的实例变量
外部静态的类变量
同名变量(外部类)
我是外部类的实例方法
我是外部类的静态方法
如果需要在外部类以外的地方创建非静态内部类的子类,则尤其要注意上面的规则:非静态内部类的构造器必须通过其外部类的对象来调用。
当创建一个子类时,子类构造器总是会调用父类构造器。例如以下程序:
public class SubClass extends Outer.Inner
{
public SubClass(Outer out)
{
//通过传入Outer对象显式调用Inner的构造器
out.super();//输出内部类的构造器
}
}
如果有非静态内部类子类的对象存在,则一定存在与子对应的外部类对象。
小结
- 可以是任何的访问修饰符。
- 内部类的内部不能有静态信息。
- 内部类也是类,该继承继承,该重写重写,该重载重载,this和super随便用。
- 外部类如何访问内部类信息,必须new之后打点访问。
- 内部类可以直接使用外部类的任何信息,如果属性或者方法发生冲突,调用外部类.this.属性或者方法。
(2)静态内部类
package com.company;
public class Outer {
private String outerVariable = "外部类实例变量";
private String commonVariable = "同名变量(外部类)";
private static String outerStaticVariable = "外部类静态变量";
{
System.out.println("外部类的构造块被执行了");
}
static {
System.out.println("外部类的静态块被执行了");
}
public void outerMothod() {
System.out.println("我是外部类的方法");
}
public static void outerStaticMethod() {
System.out.println("我是外部类的静态方法");
}
public static class Inner {
private String innerVariable = "内部类实例变量";
private String commonVariable = "同名变量(内部类)";
private static String innerStaticVariable = "内部类的静态变量";
{
System.out.println("内部类的构造块执行了");
}
static {
System.out.println("内部类的静态块执行了");
}
public void innerShow() {
System.out.println(innerVariable);
System.out.println(commonVariable);
System.out.println(outerStaticVariable);
outerStaticMethod();
}
public static void innerStaticShow() {
outerStaticMethod();
System.out.println(outerStaticVariable);
}
}
/**
* 外部类的内部如何和内部类打交道
*/
public static void callInner() {
System.out.println(Inner.innerStaticVariable);
Inner.innerStaticShow();
}
public static void main(String[] args) {
Outer.Inner in=new Outer.Inner();
in.innerShow();
}
}
其他类使用成员内部类
public class Other {
public static void main(String[] args) {
//访问静态内部类的静态方法,内部类被加载,此时外部类未被加载,独立存在,不依赖于外围类。
Outer.Inner.innerStaticShow();
//访问静态内部类的成员方法
Outer.Inner in = new Outer.Inner();
in.innerShow();
}
}
输出为
内部类的静态块执行了
外部类的静态块被执行了
我是外部类的静态方法
外部类静态变量
内部类的构造块执行了
内部类实例变量
同名变量(内部类)
外部类静态变量
我是外部类的静态方法
小结
- 内部可以包含任意的信息。
- 静态内部类的方法只能访问外部类的static关联的信息。
- 利用 外部类.内部类 引用=new 外部类.内部类(); 然后利用引用.成员信息(属性、方法)调用。
- 访问内部类的静态信息,直接外部类.内部类.静态信息就可以了。
- 静态内部类可以独立存在,不依赖于其他外围类。
- 访问静态内部类的静态方法,内部类被加载,此时外部类未被加载,独立存在,不依赖于外围类。
(三)局部内部类
public class Outer {
public static void main(String[] args) {
//定义局部内部类
class Inner
{
int a;
}
class Innersub extends Inner
{
int b;
}
//创建局部内部类的对象
var is =new Innersub();
is.a=5;
is.b=6;
System.out.println("a="+is.a+","+"b="+is.b);
}
}
输出结果
a=5,b=6
小结
- 因为局部类仅在方法里有效,所以局部内部类也不能使用访问控制符和static修饰符修饰。
- 局部成员的作用域是在所在方法。
- 局部内部类引用所在方法内被赋值却未改变的变量会报错。
- 可以随意的访问外部类的任何信息。
(四)匿名内部类
interface Product
{
double getPrice();
String getName();
}
public class Test {
public void test(Product p) {
System.out.println("购买了" + p.getName() + ",花了" + p.getPrice());
}
public static void main(String[] args) {
var ta = new Test();
//此处传入其匿名实现类的实例
ta.test(new Product() {
{
System.out.println("匿名内部类的构造块");
}
public double getPrice() {
return 100;
}
public String getName() {
return "键盘";
}
}
);
}
}
输出为:
匿名内部类的构造块
购买了键盘,花了100.0
小结
- 匿名内部类不能是抽象类,系统在创建匿名内部类时,会立即创建匿名内部类的对象。所以无法定义为抽象类。
- 匿名内部类不能定义构造器。因为匿名内部类没有类名,所以无法定义。
- 可以定义初始化块。
- 匿名内部类是方便某些只用使用一次的接口实现类。