一、什么是内部类
将一个类放在另外一个类(外部类,OuterClass)的内部定义,这就是内部类(InnerClass)。
二、为什么要使用内部类
对于初学者而言,用到内部类的情况,并不是很多。随着编码能力的提高,定会体会到它的魅力所在。它能够更优雅的设计我们的程序结构。设计模式章节也讲用到内部类的写法。
二、成员内部类
2.1、如何定义成员内部类
class Outer{
private String msg = "Hello World!";
class Inner{
public void print(){
System.out.Println(msg);
}
}
public void fun(){
// 在外部类之中实例化内部类对象,并且调用print()方法
new Inner().print();
}
}
以上就是定义了一个内部类。
2.2、内部类对象如何创建
public class TestDemo{
public static void main(String args[]){
Outer out = new outer();
out.fun();
}
}
这个时候虽然牺牲了程序的结构,但是至少它达到了一个很重要的目的。让内部类可以访问外部类中定义的私有属性内容。
内部类有一个最大的优点:可以方便的访问外部类的私有操作。
但是需要注意的是,虽然内部类可以访问内部类的私有属性,反之,外部类也可以通过内部类对象轻松的访问内部类的私有属性。
2.3、使用外部类访问内部类的私有属性
class Outer{
private String msg = "Hello World!";
class Inner{
private String info = "世界,你好";
public void print(){
System.out.println(msg);
}
}
public void fun(){
// 在外部类之中实例化内部类对象,并且调用print()方法
Inner inner = new Inner();
// 直接使用内部类对象访问内部类中的私有成员
System.out.println(inner.info);
}
}
一旦使用了内部类之后,私有属性的访问就变得非常的简单了。一直要求过,如果要访问属性前面一定要加this,但是直接在内部类的方法里面加上this表示的是查找本类的属性,但是如果此时要访问实际上是外部类的属性,那么就应该使用外部类.this.属性
来完成。
2.4、使用this来访问外部类和内部类的属性
class Outer{
private String msg = "Hello World!";
class Inner{
private String info = "世界,你好";
public void print(){
// 外部类.this = 外部类的当前对象,可以写成System.out.println(msg);
System.out.println(Outer.this.msg);
System.out.println(msg);
System.out.println("----------------");
System.out.println(this.info);
}
}
public void fun(){
// 在外部类之中实例化内部类对象,并且调用print()方法
Inner inner = new Inner();
inner.print();
}
}
public class TestDemo {
public static void main(String[] args) {
Outer out = new Outer();
out.fun();
}
}
以上代码的特点:通过外部类的一个fun()
访问了内部类的操作。内部类能不能像普通对象那样直接在外部直接产生实例化对象调用呢?
如果要想解决此类问题,俺么必须通过内部类的问题形式来观察。发现内部类的class文件形式:外部类$内部类.class。
所有的”$”是在文件中的命名,如果换回到了程序里面就变为了“.”,也就是说内部类的名称是“外部类.内部类”。
2.5、在TestDemo
之中创建内部类对象实例
class Outer{
private String msg = "Hello World!";
class Inner{
private String info = "世界,你好";
public void print(){
// 外部类.this = 外部类的当前对象,可以写成System.out.println(msg);
System.out.println(Outer.this.msg);
System.out.println(msg);
System.out.println("----------------");
System.out.println(this.info);
}
}
public void fun(){
// 在外部类之中实例化内部类对象,并且调用print()方法
Inner inner = new Inner();
inner.print();
}
}
public class TestDemo {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.print();
}
}
内部类不可能离开外部类的实例化对象,所以一点要新实例化外部类对象后才可以使用内部类对象。如果真的使用到了内部类,也基本上不会像以上的操作那样。一定是通过外部类访问内部类。
如果现在一个内部类只希望被一个外部类访问,那么可以使用private
进行声明。此时的内部类是不可能在外部进行对象实例化的。如果内部类使用private
声明了,你非要在外部类中去实例化内部类对象,将抛出如下错误信息(示例);
TestDemo.java:25: 错误: Outer.Inner 在 Outer 中是 private 访问控制
Outer.Inner inner = new Outer().new Inner();
^
TestDemo.java:25: 错误: Outer.Inner 在 Outer 中是 private 访问控制
Outer.Inner inner = new Outer().new Inner();
三、静态内部类
3.1、静态内部类介绍
使用static
定义的属性或者是方法是不受到类实例化对象控制的,所以如果使用了static
定义内部类。它一定不可能受到外部类的实例化对象控制。
如果一个内部类使用了static
定义的话,那么这个内部类就变为了一个外部类,并且只能够访问外部类中定义的static
操作。相当于定义了一个外部类。
由于静态内部类不受到外部类实例化对象的控制,那么我创建在TestDemo
类之中创建内部类实例化对象的时候,可以采用如下的形式创建:外部类. 内部类 对象 = new 外部类.内部类();
范例:静态内部类实例化对象
class Outer{
private static String msg = "Hello world!";
static class Inner{
public void print(){
System.out.println(msg);
}
}
}
public class TestDemo {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.print();
}
}
此时不再需要先产生外部类对象,再产生内部类对象,仿佛就变为了一个独立的类。如果以后看见可以直接实例化类.类.类
的时候一定要知道,这个使用了static
定义了的静态内部类。
3.2、静态内部类与普通内部类的区别
静态内部类不依赖于外部类实例被实例化,而普通内部类需要在外部类实例化之后才能实例化;
普通内部类对象隐含地保存了一个引用,指向创建它的外部类对象,静态内部类不需要其外部类的对象,静态内部类对象不能访问非静态的外部类对象成员;
静态内部类可以有静态成员,非静态内部类不能有静态成员;
四、方法内部类(局部内部类)
4.1、方法中定义内部类
内部类可以在任意的位置上定义,包括类中、代码块里面、方法里面,其中方法里面定义内部类是表常见的形式,如果后期要使用,也会使用到此类形式。
范例:在方法里面定义内部类
class Outer{
private static String msg = "Hello world!";
public void fun(){
// 定义方法内部类
class Inner{
public void print(){
System.out.println(Outer.this.msg);
}
}
new Inner().print();
}
}
public class TestDemo {
public static void main(String[] args) {
new Outer().fun();
}
}
通过查看字节码可以发现方法内部类的字节码形式为Outer$1Inner.class
。
4.2、方法内部类访问方法里面定义的参数或变量
在方法之中可以定义参数和局部变量,那么方法内部类是如何访问方法参数和方法的局部变量呢
范例:方法内部类方法里面定义的参数或变量
class Outer{
private static String msg = "Hello world!";
public void fun(int num){ //方法承诺书
// 方法局部变量
double score = 99.9;
// 方法中定义的内部类
class Inner{
public void print(){
System.out.println("属性:" + Outer.this.msg);
System.out.println("方法参数:" + num);
System.out.println("方法局部变量参数:" + score);
}
}
new Inner().print();
}
}
public class TestDemo {
public static void main(String[] args) {
new Outer().fun(1);
}
}
此时发现没有加入任何的修饰,方法中的内部类可以访问方法的参数以及定义的变量。但是这种操作只适合JDK1.8之后的版本。如果是在JDK1.7以及之前的版本有一个严格的要求:方法中定义的内部类如果要想访问方法的参数或者是方法定义的变量,那么参数或变量前一定要加上final
关键字标记。
虽然有新特性,但是代码还是要写完整。由于存在这个严格要求,实际上意味着方法内部类不能去修改方法参数和方法的局部变量值。
范例:JDK1.7及以前版本示例
class Outer{
private static String msg = "Hello world!";
public void fun(final int num){ //方法承诺书
// 方法局部变量
final double score = 99.9;
// 方法中定义的内部类
class Inner{
public void print(){
System.out.println("属性:" + Outer.this.msg);
System.out.println("方法参数:" + num);
System.out.println("方法局部变量参数:" + score);
}
}
new Inner().print();
}
}
public class TestDemo {
public static void main(String[] args) {
new Outer().fun(1);
}
}
五、匿名内部类
5.1、问题引出
正常的规律就是一个接口或者是抽象类需要有子类,子类要覆写所有的抽象方法
可是如果说现在MessageImpl
子类只使用唯一的一次。那么我们还有必要将其定义为一个单独的类嘛。所以这个时候就可以采用匿名内部类的方式进行代码的简化。
范例:观察如下常规操作
// 定义消息接口
interface Message{
public void print();
}
// 定义消息接口子类
class MessageImpl implements Message{
@Override
public void print(){
System.out.println("Hello World!");
}
}
// 程序入口类
public class TestDemo{
public static void main(String[] args){
fun(new MessageImpl());
}
public static void fun(Message msg){
msg.print();
}
}
5.2、使用匿名内部类来简化代码
使用匿名内部类的时候有一个前提:必须要基于接口或抽象类的应用。但是需要强烈强调的是,如果匿名内部类定义在方法里面,方法的参数或者是变量要被匿名内部类所访问,那么必须加上final
关键字(JDK1.8之后此要求被改变)。
匿名内部类是在抽象类和接口的基础上发展的,匿名内部类的最大好处是帮助用户减少了类的定义。
范例:使用匿名内部类来简化代码
// 定义消息接口
interface Message{
public void print();
}
// 程序入口类
public class TestDemo{
public static void main(String[] args){
fun(new Message(){
@Override
public void print(){
System.out.println("hello world!");
}
});
}
public static void fun(Message msg){
msg.print();
}
}