JavaSE 22 内部类的概念与分类

第二十七章 内部类的概念与分类

如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。
例如:汽车与发动机的关系

  • 分类:
  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

27.1 成员内部类

  • 格式:
修饰符 class 外部类名称 {
	修饰符 class 内部类名称 {
		//...
	}
	//...
}
  • 例子:
public class Body {//外部类
	
	public class Heart {//成员内部类
	
		public viod beat() {//内部类方法
			System.out.println("beat,beat!");
			System.out.println(name);//正确;能够访问外部成员变量
		}
		
	}
	
	private String name;
	
	public void methodBody() {
		System.out.println("外部类的方法");
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
}

注意:内用外,可以随意访问;外用内,需要内部类对象。

27.1.1 成员内部类的使用

  1. 间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
public class Body {//外部类
	
	public class Heart {//成员内部类
	
		public viod beat() {//内部类方法
			System.out.println("beat,beat!");
			System.out.println(name);//正确;能够访问外部成员变量
		}
		
	}
	
	private String name;
	
	public void methodBody() {
		System.out.println("外部类的方法");
		new Heart().beat();
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
}
public class Demo1 {
	
	public static void main(String[] args) {
		Body body = new Body();
		//通过外部类的对象,调用外部类的方法,里面间接在使用内部类Heart
		body.methodBody();
	}
	
}
  1. 直接方式:公式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
public class Demo2 {
	
	public static void main(String[] args) {
		Body.Heart heart = new Body().new Heart();
		heart.beat();
	}
	
}

27.1.2 内部类的同名变量访问

  • 格式:
    外部类名称.this.外部成员变量名
public class Outer {
	
	int num = 10;//外部类的成员变量
	
	public class Inner {
		int num = 20;//内部类的成员变量
	
		public void methodInner() {
			int num = 30;//内部类方法的局部变量
			System.out.println(num);//30局部变量;就近原则
			System.out.println(this.num);//20
//			System.out.println(super.num);//错误;内部类和外部类并不是继承关系
			System.out.println(Outer.this.num);//外部类的成员变量名
		}
	}
}
public class Demo {
	
	public static void main(String[] args) {
		Outer.Inner obj = new Outer().new Inner();
		obj.merhodInner();
	}
	
}

27.2 局部内部类的定义

如果一个类是定义在方法内部的,那么这就是一个局部内部类。
”局部“:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

  • 定义格式:
修饰符 class 外部类名称 {
	修饰符 返回值类型 外部类方法名称(参数列表) {
		class 局部内部类名称 {
			//...
		}
	}
}
  • 例子:
public class Outer {
	
	public viod methodOuter() {
		class Inner {	//局部内部类
			int num = 10;
			public void methodInner() {
				System.out.println(num);//10
			}
		}
		
		Inner inner = new Inner();
		inner.methodInner();	//只有当前方法才能访问
	}
	
}
public class Demo {

	public static void main(String[] args) {
		Outer obj = new Outer();
		obj.methodOuter();
	}

}

  • 小节一下类的权限修饰符:
    public > protected > (default) > private
    定义一个类的时候,权限修饰符规则:
  1. 外部类: public / (default)
  2. 成员内部类:public / protected / (default) / private
  3. 局部内部类:(default)

27.2.1 局部内部类的 final 问题

局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】

【备注】:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略

  • 原因:
  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中。
  3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
  5. 只要一直保持局部变量不变就会被复制到内部类当中当成成常量。
public class MyOuter {

	public void methodOuter() {
		final int num = 10;//final可以省略但是num不能再重新赋值,否则局部类就不能访问
		
		class Inner {
			public void methodInner() {
				System.out.println(num);
			}
		}
	}
}

27.3 匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次。那么这种抢矿下就可以省略掉该类的定义,而改为使用匿名内部类。

  • 格式:(末尾的冒号不要丢了)
接口名称 对象名称 = new 接口名称() {
	//覆盖重写所有抽象方法
};
  • 例子
  1. 常规方法:
public interface MyInterface {
	
	/*public abstract*/ void method();//抽象方法
	
}
public class MyInterfaceImpl impelememts MyInterface {
	@Override
	public void method() {
		System.out.println("实现类覆盖重写了方法");
	}
}
public class Demo1 {
	public static void main(String[] args) {
		MyInterface obj = new MyInterfaceImpl();//多态
		obj.method();
	}
}
  1. 匿名内部类
public interface MyInterface {
	
	/*public abstract*/ void method();//抽象方法
	
}
public class Demo2 {

	public static void main(String[] args) {
		MyInterface obj = new MyInterface() {//这个大括号里就是个匿名内部类
			@Override
			public void method() {
				System.out.println("匿名内部类实现了方法A");
			}
		};
		obj.method();
		
		new MyInterface() {//使用方法2
			@Override
			public void method() {
				System.out.println("匿名内部类实现了方法B");
			}
		}.method();
	}
	
}

  • 格式解析:“new 接口名称() {…};”
  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现哪个接口
  3. {…}这才是匿名内部类的内容
  • 注意事项:
  1. 匿名内部类,在 创建对象 的时候,只能使用唯一一次。(如果希望多次创建对象,而且类的内容一样的化,那么久必须使用单独定义的实现类)
  2. 匿名对象,在 调用方法 的时候,只能调用唯一一次。(如果希望同一个对象,调用多次就需要给对象取个名字)
  3. 匿名内部类是省略了 实现类/子类名称 ,但是匿名对象是省略了 对象名称(匿名内部类和匿名对象不是一回事)

你可能感兴趣的:(Java)