学习要点
- 内部类的定义
- 内部类的应用
内部类
定义
Java的一个类中包含着另一类。
A类和B类是C类的外部类。B类是C类的外部类。A类也称为顶层类。
如何使用内部类
public class MyFatherClass { } public interface MyInterface { public abstract int getValue(); } public class MyClass extends MyFatherClass implements MyInterface { int value = 0; public int getValue() { return value; } public String getValue() { return "这是一个神奇的时代!"; } }
以上代码会存在什么问题?编译器提示出错,提示方法重复。即编译器搞不清楚getValue()是继承接口重写的还是重载的。如何解决?使用内部类。
public class MyClass extends MyFatherClass { int value = 0; public int getValue() { return value; } public class MyInnerClass implements MyInterface { public int getValue() { return 0; } } }
内部类可以访问外部类的所有资源,如果内部类是继承其他类或者接口,不影响外部类的继承和接口实现。轻松实现Java的“多继承”。
例如:
class A{} class B{} public class S extends A { public class E extends B{ } }
内部类主要作用
- 访问外部类所有成员,包括private成员。
- 多重继承。
- 封装类型。
- 编写事件驱动程序。
- 回调。
内部类的分类
- 内部类主要分为成员内部类、局部内部类。
- 其中,成员内部类根据数据访问权限的不同,又分为静态内部类和非静态内部类。
- 还有一种称为匿名内部类,匿名内部类在程序运行过程中只使用一次,用完即销毁。
- 成员内部类和类的属性、方法、构造方法一样,属于类的成员。而局部内部类或者匿名内部类则不属于类的成员。
- 因此,成员内部类就同其他成员一样,可以采用private、public等修饰,可以被继承。
- 例如:直接把抽象类Person在Demo类中实现了。
abstract class Person { public abstract void read(); } public class MyClass { public static void main(String[] args) { Person p = new Person() {//匿名内部类 public void read() { System.out.println("读书!"); } }; p.read(); } }
内部类的语法结构
[访问修饰符] 外部类类名{ ……… [访问修饰符] 内部类类名{ …… } …… }
语法示例
/**外部类A*/ public class A { private String mess = "这是外部类私有成员变量mess"; /**内部类B*/ class B { public String getMess() { return mess; } } private B b = new B();//类A的私有成员变量 /**通过外部类访问内部类获取信息方法*/ public void showMess() { System.out.println(b.getMess()); } /**测试方法*/ public static void main(String[] args){ A a=new A(); a.showMess(); } }
直接访问内部类
/**测试方法*/ public static void main(String[] args){ //A a=new A(); //a.showMess(); A.B in=new A().new B();//如果内部类不希望被外部访问,使用private修饰 System.out.println(in.getMess()); }
非静态内部类
定义
非静态内部类不采用static修饰。
特性
内部类和外部类同名成员变量的访问
示例代码:
class Outer { int number=2000; public class Inner{ int number=1888; public int getNum(){ return number; } } } public class Test{ public static void main(String[] args){ Outer.Inner in = new Outer().new Inner(); System.out.println(in.getNum()); } }
在测试类中,输出的number值是多少?---1888
当内部类的方法访问同名变量时,采用就近原则,先访问最近的一个成员,如果满足条件,就取最近一个成员变量的值,如果不满足,再依次向外部类寻找同名变量。如果找不到,编译器则报错。
外部类访问内部类成员
需要显示的实例化非静态内部类,通过对象名访问内部类成员。例如语法示例中的代码:
/**外部类A*/ public class A { private String mess = "这是外部类私有成员变量mess"; /**内部类B*/ class B { public String getMess() { return mess; } } private B b = new B();//类A的私有成员变量 /**通过外部类访问内部类获取信息方法*/ public void showMess() { System.out.println(b.getMess()); } /**测试方法*/ public static void main(String[] args){ A a=new A(); a.showMess(); } }
非静态内部类不允许有静态成员:包括静态属性、方法、代码块。
外部类的静态成员不能访问非静态内部类。
静态内部类(嵌套内部类)
定义
静态内部类采用static修饰,他是属于类成员。而非静态内部类则属于类的某个对象。
语法结构
class Outer { public static class Inner{ } }
特性
静态内部类不能访问外部非静态成员,只能访问外部类的静态成员,即类成员。
示例代码:
class Outer { static int number=1000; public static class Inner{ static void printNumber(){ System.out.println(number); } } } public class Test{ public static void main(String[] args){ Outer.Inner.printNumber(); } }
实例化静态内部类,不需要创建外部类引用。
示例代码:
class Outer { static int number=1000; public static class Inner{ static void printNumber(){ System.out.println(number); } } } public class Test{ public static void main(String[] args){ //Outer.Inner in = new Outer().new Inner(); Outer.Inner in = new Outer.Inner(); in.printNumber(); } }
局部内部类
定义
如果将一个类定义在一个方法内部,则称为局部内部类。和局部变量类似,局部内部类是不允许采用访问修饰符的。(局部变量只允许使用final修饰)。
特性
局部内部类不能在外部类以外的地方使用。局部内部类的作用域只在方法内部,方法外部无法访问局部内部类。
示例代码
class Outer { int number = 1000; public void method(){ class LocalInner{ int count=10; } LocalInner localInner=new LocalInner(); System.out.println("count="+localInner.count); } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } }
局部内部类可以访问外部类的所有资源
局部内部类只能访问所在方法final修饰的局部变量(包括方法的形参)
示例代码:
class Outer { int number = 1000; public void method(){ int money=100;//局部内部类无法访问局部变量 final int score=168;//局部内部类可以访问final修饰的局部变量 class LocalInner{ int count=10; public void printNum(){ System.out.println(number);//局部内部类访问外部类成员 System.out.println(money); //无法访问,报错 System.out.println(score); } } LocalInner localInner=new LocalInner(); System.out.println("count="+localInner.count); localInner.printNum(); } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } }
匿名内部类
定义
匿名内部类是一种特殊的内部类,没有名字。
例如:
public class Test { public static void main(String[] args) { Test test = new Test(){ }; } }
在直接“实例化”接口或者抽象类的时候,可以采用匿名类的方式进行实例化。
特性
匿名内部类必须继承一个父类或者实现一个接口。当且仅当只能继承一个父类,或者实现一个接口。
匿名类定义中的示例,匿名类继承了Test类。
创建完匿名类对象,匿名类自动消失。
匿名类本身没有构造方法,但他会调用父类的构造方法。
代码示例:
public class Test { public Test(){ System.out.println("这是父类Test的构造方法!"); } public void method(){ System.out.println("这是父类普通方法!"); } public static void main(String[] args) { Test test = new Test(){ }; test.method(); }
以上代码将会输出:
匿名类本身没有构造方法,但可以有初始化代码。
代码示例:
public class Test { public Test(){ System.out.println("这是父类Test的构造方法!"); } public void method(){ System.out.println("这是父类普通方法!"); } public static void main(String[] args) { Test test = new Test(){ {System.out.println("匿名内部类的初始化代码块!");} }; test.method(); } }
初始化代码使用{}标识,其初始化的顺序类似继承关系构造方法的执行顺序,输出结果:
成员变量定义成匿名内部类
除了可以在外部类的方法中定义匿名内部类,还可以在成员变量定义匿名内部类。
abstract class AC{} public class Test { AC ac = new AC(){};//匿名类成员变量 public static void main(String[] args) { } }
封装数据类型
常见的顶层类只能用public或者默认访问修饰符,而成员内部类可以使用任意访问级别的修饰符。
如果一个类仅仅只是为一个方法提供服务,那么这个类就可以定义为方法内的局部内部类,只在该方法内可见。
面向对象核心思想是封装,我们已经学习了属性和方法的封装,内部类也是一种封装方式。
示例代码:
interface Tool{} public class Outer { private class InnerTool implements Tool{//实现了工具接口的内部类 } public Tool getTool(){ return new InnerTool(); } }
测试类访问不了private修饰的InnerTool内部类,但是可以通过Outer外部类提供的getTool()方法访问获得InnerTool对象。从而实现数据类型的封装,即把Tool类型封装成InnerTool类型。
回调(闭包)
定义
某个方法获得内部类对象的引用后,就可以在合适的时候反过来调用外部类对象的引用。即,允许测试类通过内部类引用来调用其外部类的方法。
示例代码:
/**修改英语成绩接口*/ public interface ReMarkENAble { /**修改成绩*/ public abstract void modify(int score); } /** 修改语文成绩类 */ public class ReMarkCN { private int scoreCN;//语文成绩 /**修改语文分数*/ public void modify(int scoreCN) { this.scoreCN = scoreCN; } public int getScoreCN(){ return this.scoreCN; } }
问题:如果一个学生类既想修改语文成绩又想修改英语成绩要如何实现?定义一个学生类,继承ReMarkCN类,实现ReMarkENAble接口,问题是这两个类中modify方法重名,导致学生类只能实现修改语文成绩的方法。
如何实现学生类既可以修改语文成绩,又可以修改英语成绩?使用内部类回调。
示例代码:
/** 学生类 */ public class Student extends ReMarkCN { private int scoreEN;//英文成绩 public void outerModify(int scoreEN) { this.scoreEN = scoreEN; } /**闭包:接口+内部类*/ private class Inner implements ReMarkENAble { @Override public void modify(int scoreEN) { outerModify(scoreEN); } } /**获得修改英语成绩内部类对象*/ public ReMarkENAble getReMarkENAble(){ return new Inner(); } /**打印成绩单*/ public void print(){ System.out.println("*****成绩单*****"); System.out.println("语文成绩:"+this.getScoreCN()); System.out.println("英语成绩:"+this.scoreEN); } } /**测试类*/ public class Test { public static void main(String[] args) { Student student=new Student(); //修改语文成绩 student.modify(60); //修改英语成绩 ReMarkENAble reMarkENAble=student.getReMarkENAble(); reMarkENAble.modify(61); //输出成绩 student.print(); } }