在Java中,允许在一个类的内部定义类,这样的类称作内部类(inner class),这个内部类所在的类称作外部类(outer class)。
在一个类中除了可以定义成员变量、成员方法,还可以定义类,这样的类被称作成员内部类。其实我们可以将内部类理解为国中之国。
在成员内部类中,可以访问外部类的所有成员,包括成员变量和成员方法;在外部类中,同样可以访问成员内部类的变量和方法。
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
package net.hw.lesson16.member;
/**
* 功能:演示成员内部类
* 作者:华卫
* 日期:2020年05月12日
*/
public class School {
// 定义外部类成员变量
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 定义外部类成员方法
public void speak() {
System.out.println("欢迎参观" + name);
}
// 定义成员内部类
class Teacher {
// 定义成员内部类成员变量
private String name;
private int age;
// 定义成员内部类成员方法
public void work() {
// 访问外部类成员方法
speak();
// 访问外部类成员变量
System.out.println("我在" + School.this.name + "工作。");
}
}
// 定义外部类成员方法,访问成员内部类成员变量与方法
public void test() {
// 创建成员内部类对象
Teacher teacher = new Teacher();
// 访问成员内部类成员变量
teacher.name = "李晓红";
teacher.age = 20;
System.out.println("我叫" + teacher.name + ",今年" + teacher.age + "岁了。");
// 访问成员内部类成员方法
teacher.work();
}
}
说明:内部类要访问外部类同名变量,采用外部类名.this.外部类同名变量
,比如第35行代码:System.out.println(“我在” + School.this.name
+ “工作。”); 当然也可以使用new School().name
方式来访问外部类成员变量。
package net.hw.lesson16.member;
/**
* 功能:测试School类
* 作者:华卫
* 日期:2020年05月12日
*/
public class TestSchool {
public static void main(String[] args) {
// 创建外部类对象
School school = new School();
// 设置外部类对象属性
school.setName("泸州职业技术学院");
// 基于外部类对象创建成员内部类对象
School.Teacher teacher = school.new Teacher();
// 调用成员内部类对象的成员方法(访问外部类成员变量与方法)
teacher.work();
// 调用外部类对象的成员方法(访问成员内部类成员变量与方法)
school.test();
}
}
局部内部类,也叫做方法内部类,就是定义在某个局部范围中的类,它和局部变量一样,都是在方法中定义的,其有效范围只限于方法内部。
在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中的变量和方法却只能在创建该局部内部类的方法中进行访问。
package net.hw.lesson16.local;
/**
* 功能:演示局部内部类
* 作者:华卫
* 日期:2020年05月12日
*/
public class School {
// 定义外部类成员变量
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 定义外部类成员方法
public void speak() {
System.out.println("欢迎参观" + name);
}
// 定义外部类成员方法,在里面定义局部内部类
public void test() {
// 定义局部内部类
class Teacher {
// 定义局部内部类成员变量
private String name;
private int age;
// 定义局部内部类成员方法
public void work() {
// 访问外部类成员方法
speak();
// 访问外部类成员变量
System.out.println("我在" + School.this.name + "工作。");
}
}
// 在局部内部类所在方法里创建局部内部类对象
Teacher teacher = new Teacher();
// 访问局部内部类成员变量
teacher.name = "李晓红";
teacher.age = 20;
System.out.println("我叫" + teacher.name + ",今年" + teacher.age + "岁了。");
// 访问局部内部类成员方法
teacher.work();
}
}
在外部类School里定义了成员变量name、成员方法speak()与test(),并在test()里创建了局部内部类Teacher,然后在局部内部类Teacher里创建了work()方法来测试对外部类变量和方法的调用,同时,还在test()方法测试对局部内部类变量和方法的调用。
package net.hw.lesson16.local;
/**
* 功能:测试School类
* 作者:华卫
* 日期:2020年05月12日
*/
public class TestSchool {
public static void main(String[] args) {
// 实例化外部类对象
School school = new School();
// 设置对象属性
school.setName("泸州职业技术学院");
// 调用外部类普通方法
school.speak();
// 通过外部类对象调用创建了局部内部类的方法
school.test();
}
}
运行程序,查看效果:
但是要注意,只有创建了局部内部类的方法才能访问局部内部类对象的成员变量和方法,外部类甚至是无法访问局部内部类对象的,因为局部内部类只是在创建它的成员方法里有效,一旦越出创建局部内部类的方法,局部内部类就失效了。
所谓静态内部类,就是使用static关键字修饰的成员内部类。
静态内部类在成员内部类前增加了static关键字,在功能上,静态内部类中只能访问外部类的静态成员,同时通过外部类访问静态内部类成员时,可以跳过外部类从而直接通过内部类访问静态内部类成员。
外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名();
package net.hw.lesson16.static_;
/**
* 功能:演示静态内部类
* 作者:华卫
* 日期:2020年05月13日
*/
public class School {
// 定义外部类静态成员变量
static String name = "泸州职业技术学院";
// 定义外部类静态成员方法
public static void speak() {
System.out.println("欢迎访问" + name);
}
// 定义静态内部类
static class President {
// 定义静态内部类静态成员变量
private static String name = "杨宗伟";
private static int age = 56;
// 定义静态内部类成员方法
public void work() {
// 访问外部类静态成员方法
speak();
// 访问外部类静态成员变量
System.out.println("我在" + School.name + "工作。");
}
// 定义静态内部类静态成员方法
public static void speak() {
System.out.println("我是校长,叫" + name + ",今年" + age + "岁了。");
}
}
// 定义外部类成员方法
public void test() {
// 访问静态内部类静态成员变量
System.out.println("校长叫" + President.name + ",今年" + President.age + "岁了。");
// 通过静态内部类对象去访问静态内部类成员方法
new President().work(); // President.work() 编译要报错
// 通过静态内部类去访问静态内部类静态成员方法
President.speak();
}
}
package net.hw.lesson16.static_;
/**
* 功能:测试School类
* 作者:华卫
* 日期:2020年05月13日
*/
public class TestSchool {
public static void main(String[] args) {
// 访问外部类静态成员变量
System.out.println("学校名称:" + School.name);
// 通过外部类名访问静态成员方法
School.speak();
// 通过外部类对象访问非静态成员方法
new School().test();
// 基于外部类名创建静态内部类对象
School.President president = new School.President();
// 通过静态内部类对象访问非静态成员方法
president.work();
// 通过静态内部类对象访问静态成员方法
president.speak();
// 通过静态内部类名访问静态成员方法
School.President.speak();
}
}
运行程序,查看结果:
说明:外部类School成员方法test()不能通过静态内部类名访问其非静态成员方法work():
匿名内部类其实就是没有名称的内部类。
在调用包含有接口类型参数的方法时,通常为了简化代码,可以直接通过匿名内部类的形式传入一个接口类型参数,在匿名内部类中直接完成方法的实现。
从JDK 8开始,允许在局部内部类、匿名内部类中访问非final修饰的局部变量,而在JDK 8之前,局部变量前必须加final修饰符,否则程序编译报错。
new 父接口(){ // 匿名内部类实现部分 }
package net.hw.lesson16.anonymous;
/**
* 功能:动物接口
* 作者:华卫
* 日期:2020年05月13日
*/
public interface Animal {
void eat();
}
package net.hw.lesson16.anonymous;
/**
* 功能:学生类
* 作者:华卫
* 日期:2020年05月13日
*/
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Animal animal) {
System.out.println(name + "喂养一只动物。");
animal.eat();
}
}
package net.hw.lesson16.anonymous;
import java.util.Scanner;
/**
* 功能:测试Student类
* 作者:华卫
* 日期:2020年05月13日
*/
public class TestStudent {
public static void main(String[] args) {
// 声明变量
String studentName, animalName;
Scanner sc = new Scanner(System.in);
// 输入部分
System.out.print("学生姓名:");
studentName = sc.next();
System.out.print("动物昵称:");
animalName = sc.next();
// 创建学生对象
Student student = new Student();
// 设置学生对象属性
student.setName(studentName);
// 调用对象方法,创建匿名接口对象作为参数
student.feed(new Animal() {
@Override
public void eat() {
// JDK1.8开始,局部内部类与匿名内部类可以访问非final局部变量
System.out.println("狗崽[" + animalName + "]在啃骨头。" );
}
});
}
}
运行程序,查看结果:
创建匿名接口对象作为方法参数,在我们后面的学习与项目实训中会反复用到,请大家务必理解并掌握。
当然刚才完成的任务,也可以采用局部内部类来完成。
package net.hw.lesson16.anonymous;
import java.util.Scanner;
/**
* 功能:测试Student类
* 采用局部内部类
* 作者:华卫
* 日期:2020年05月13日
*/
public class TestStudent1 {
public static void main(String[] args) {
// 声明变量
String studentName, animalName;
Scanner sc = new Scanner(System.in);
// 输入部分
System.out.print("学生姓名:");
studentName = sc.next();
System.out.print("动物昵称:");
animalName = sc.next();
// 创建学生对象
Student student = new Student();
// 设置学生对象属性
student.setName(studentName);
// 定义局部内部类,实现动物接口
class Dog implements Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println("狗崽[" + name + "]在啃骨头。" );
}
}
// 创建狗对象
Dog dog = new Dog();
// 设置狗对象属性
dog.setName(animalName);
// 调用学生对象喂养方法
student.feed(dog);
}
}
基于Car接口创建匿名内部类对象作为参数传给驾驶员对象的drive()方法。