Java基础(五)--内部类
内部类简单来说就是把一个类的定义放到另一个类的定义内部
内部类分为:成员内部类、局部内部类、匿名内部类、静态内部类
成员内部类:最常见的内部类
public class Outter { private int i = 0; public static String name = "sam"; public Outter(int i) { this.i = i; } class Inner{ public Inner() { System.out.println("Inner Class"); } public void add() { System.out.println(i + " Name: " + name); System.out.println("add()"); } } }
测试类:
public static void main(String[] args) { Outter outter = new Outter(1); Outter.Inner inner = outter.new Inner(); inner.add(); }
结果:
Inner Class 1 Name: sam add()
成员内部类Inner看起来是外部类Outter的成员,所以以此命名
内部类可以访问外部类的所有属性和方法(包含private和static)
外部类访问内部类一般就是通过上述代码的方式
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同
名成员,需要以下面的形式进行访问:this表示当前对象,如果Outter方法为static,不需要this
外部类.this.成员变量 外部类.this.成员方法
public class Outter { private int i = 0; public static String name = "sam"; public Outter(int i) { this.i = i; } private static void del() { System.out.println("Outter del()"); } class Inner{ private int i = 2; public Inner() { System.out.println("Inner Class"); } private void del() { System.out.println("Inner del()"); } public void add() { del(); Outter.del(); System.out.println("Inner Class: " + i + " Name: " + name); System.out.println("Outter Class: " + Outter.this.i + " Name: " + name); System.out.println("add()"); } } }
内部类的修饰符可以是任意的,但是如果为private,只能在外部类的内部进行访问,其他地方不可以。这一点不同和外部类,外部类的修饰符
只能是public和默认,应该是因为此时的内部类相当于外部类的成员,而成员可以是任意的
局部内部类:
定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
为什么要使用局部内部类?
1、实现某个接口,可以创建并返回对其的作用
2、要解决一个复杂的问题,创建一个类来辅助解决,但又不希望这个类是公共可用的
内部类位于方法内:
public interface Destination {
String readLine();
}
public class Outter { public Destination destination(String s) { class Inner1 implements Destination { private String label; public Inner1(String whereTo) { this.label = whereTo; } @Override public String readLine() { return label; } } return new Inner1(s); } public static void main(String[] args) { Outter outter = new Outter(); Destination destination = outter.destination("aaa"); } }
Inner属于Destination,而不是Outter的一部分,所以在destination()之外无法访问Inner
也可以位于作用域内,例如if()
PS:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的
PS:在jdk1.8之前这个参数s需要final修饰,否则会编译报错,jdk1.8不需要
匿名内部类:
public class Outter { public Destination destination() { return new Destination() { //插入一个类的定义 private int i = 11; public int value() { return i; } }; } public static void main(String[] args) { Outter outter = new Outter(); Destination destination = outter.destination(); } }
在讲接口的时候,曾经说过,接口无法被实例化,直接new只是生成一个匿名内部类而已。
这里产生一个类,这个类是匿名的。创建了一个继承自Destination的匿名类对象,返回的引用自动向上转型为对Destination的引用。
下面是简化形式:
public class Outter { public Destination destination() { return new MyDestination(); } class MyDestination implements Destination { private int i = 11; public int value() { return i; } } }
PS:简化形式有点像成员内部类,但是这里区别就是class要实现一个接口
传递参数:
public class Outter { public Destination destination(int i1) { return new Destination() { //插入一个类的定义 private int i = i1; public int value() { return i1; } }; } public static void main(String[] args) { Outter outter = new Outter(); Destination destination = outter.destination(2); } }
PS:在jdk1.8之前这个参数i1需要final修饰,否则会编译报错,jdk1.8不需要,和局部内部类相同
唯一一个没有构造器的类,感觉这句话有问题,通过javap查看字节码指令,发现编译器会为匿名内部类生成类名,带有引用的构造函数
匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类(抽象类)或是实现接口,并不需要增加额外的方法
,只是对继承方法的实现或是重写。
应用:
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("run()"); } };
还有就是添加监听,进行接口回调,这个很常见,例如在rabbitMQ中使用confirm和return机制
//6 添加一个return监听 channel.addReturnListener(new ReturnListener() { @Override public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException { System.err.println("---------handle return----------"); System.err.println("replyCode: " + replyCode); System.err.println("replyText: " + replyText); System.err.println("exchange: " + exchange); System.err.println("routingKey: " + routingKey); System.err.println("properties: " + properties); System.err.println("body: " + new String(body)); } }); //6 添加一个确认监听 channel.addConfirmListener(new ConfirmListener() { @Override public void handleNack(long deliveryTag, boolean multiple) throws IOException { System.err.println("-------no ack!-----------"); } @Override public void handleAck(long deliveryTag, boolean multiple) throws IOException { System.err.println("-------ack!-----------"); } });
静态内部类:
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。如果不需要内部类对象和外部类对象有联系,可以使用static
普通内部类:内部类对象隐式地保存一个指向外部类对象的引用
静态内部类:创建对象,不需要依赖外部类对象,不能访问非静态的外部类变量和方法,因为非static依赖对象,而静态内部类又不依赖外部类对象
public class Outter { private static String s1; private String s3; static class Inner { private static String s = s1; private String s2 = s3; public static void add() { System.out.println("add()"); } public void del() { System.out.println("del()"); } }; public static void main(String[] args) { Outter.Inner inner = new Outter.Inner(); inner.del(); Outter.Inner.add(); } }
结果:
1、印证里上面所说,静态内部类不能引用外部类非static字段,这里s2 = s3会编译报错
2、static方法和非static方法调用区别,要访问静态内部类的非static方法,必须通过静态内部类的实例对象去调用
命名规则:
匿名内部类:Outter$1.class,数字依次递增
非匿名内部类:Outter$Inner.class
除此之外还包含一个Outter.class
为什么在Java中需要内部类?
1、Java可以多实现,不能多继承,可以通过接口实现多继承,而内部类是多继承另一种实现方案。无论外部类是否继承接口,内部类都可以继承
2、方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏(可以访问外部类的private变量)
3、方便编写事件驱动程序
4、方便编写线程代码
本文参考:Java编程思想,Java内部类详解
很多内容来自海子的这篇博客,算是转载,原文也更加详细,内容更多一点,也加了一点自己的理解