1
不多说,先上个代码
Outer类里面有个内部类Inner
public class Outer {
private String msg = "哈哈"; //只能在类内部访问
public void fun(){
Inner in = new Inner(); //实例化内部类的对象
in.print();
}
//在Outer类中的内部类
class Inner{
public void print(){
System.out.println(Outer.this.msg); //msg是Outer类里面的属性
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.fun();
}
}
创建外部类的实例调用外部类的方法却执行了内部类的方法,打印出“哈哈”。
从整体的代码结构来讲,内部类的结构并不合理,所以内部类本身最大的缺陷在于破坏了程序的结构,但是破坏需要有目的的破坏,那么它也一定会有其优势,如果要想更好的观察出内部类的优势,就可以将内部类拿到外面来。我将上面的代码Inner拿出来
public class Outer {
private String msg = "哈哈"; //只能在类内部访问
public void fun(){
Inner in = new Inner(this); //实例化内部类的对象
in.print();
}
public String getMsg(){
return this.msg;
}
}
public class Inner {
private Outer out;
public Inner(Outer out){
this.out = out;
}
public void print(){
System.out.println(this.out.getMsg()); //msg是Outer类里面的属性
}
}
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
outer.fun();
}
}
如果不是对引用和扎实,会有点懵。我们折腾了半天主要的目的就是为了让Inner这个内部类可以访问Outer这个类中的私有属性,如果不用内部类的时候整体代码会非常的麻烦,所以可以得出内部类的优点:轻松的访问外部类中的私有属性。
需要注意的是,内部类虽然可以方便的访问外部类中的私有成员或私有方法,同理,外部类也可以轻松访问内部类中的私有成员或私有方法。如下
public class Outer {
private String msg = "哈哈";
public void fun(){
Inner in = new Inner();
in.print();
System.out.println(in.info); //访问内部类的私有属性
}
class Inner{
private String info = "今天天气不好";
public void print(){
System.out.println(Outer.this.msg);
}
}
}
使用了内部类之后,内部类与外部类之间的私有操作的访问就不再需要通过setter,getter以及其他的间接方式完成了,可以直接进行操作,但是需要注意的是,内部类本身也属于一个类,虽然在大部分情况下内部类往往是被外部类包裹的,但是外部依然可以产生内部类的实例化对象,而此时,内部类实例化对象的格式如下:
外部类.内部类 内部类对象 = new 外部类().new 内部类();
在内部类编译完成之后会自动形成一个“Outer$Inner.class”类文件,其中“$”这个符号换到程序中就变为“.”,所以内部类的全称:“外部类.内部类”。内部类与外部类之间可以直接进行私有成员的访问,这样一来内部类如果要是提供有实例化对象了,一定要先保证外部类实例化了。
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.print();
}
}
如果此时Inner类只允许Outer类来使用,那么在这样的情况下就可以使用private进行私有定义。
这样,此时的Inner类就无法再外部使用,即在test中的这条语句 Outer.Inner in = new Outer().new Inner()就失效。
2
在我们的java之中,类作为最基础的结构体实际上还有与之类似的抽象类或者是接口,抽象类和接口中都可以定义内部结构。
2.1
我们现在定义内部接口:
public interface IChannel {
public void send(IMessage msg);
//内部接口
interface IMessage{
public String getContent();
}
}
public class ChannelImpl implements IChannel {
@Override
public void send(IMessage msg) {
System.out.println(msg.getContent());
}
class MessageImpl implements IMessage{
@Override
public String getContent() {
return "haha";
}
}
}
public class Test {
public static void main(String[] args) {
IChannel channel = new ChannelImpl();
channel.send(((ChannelImpl)channel).new MessageImpl());
}
}
最后打印出结果 “哈哈”
2.2
下面我们继续观察一个内部抽象类,内部抽象类可以定义在普通类,抽象类,接口内部都可以
public interface IChannel {
public void send();
abstract class AbstractMessage{
public abstract String getContent();
}
}
public class ChannelImpl implements IChannel {
@Override
public void send() {
AbstractMessage msg = new MessageImpl() ;
System.out.println(msg.getContent());
}
class MessageImpl extends AbstractMessage {
public String getContent() {
return "哈哈";
}
}
}
public class Test {
public static void main(String[] args) {
IChannel channel = new ChannelImpl();
channel.send();
}
}
结果打印出来的是“哈哈”。
2.3
内部类还有一些更为有意思的结构,即:如果现在定义了一个接口,那么可以在内部利用类实现该接口,在JDK1.8之后,接口中追加了static方法可以不受到实例化对象的控制,现在就可以利用此特性来完成功能。
接口内部进行接口实现
public interface IChannel {
public void send();
class ChannelImpl implements IChannel{
public void send(){
System.out.println("哈哈");
}
}
public static IChannel getInstance(){
return new ChannelImpl();
}
}
public class Test {
public static void main(String[] args) {
IChannel channel = IChannel.getInstance();
channel.send();
}
}
输出的结果为“哈哈”
从上面可以看到,内部类是非常灵活的结构,只要你的语法满足了,各种需求都可以帮你实现!
3.static定义内部类
31.static定义内部类
如果说现在内部类上使用了static定义,那么这个内部类就变成了“外部类”,static定义的都是独立于类的结构,所以该类结构就相当于是一个独立的程序类了。需要注意,static定义的不管是类还是方法只能够访问static成员,所以static定义的内部类只能够访问外部内中的static属性和方法;
public class Outer {
private static final String MSG = "哈哈";
static class Inner{
public void print(){
System.out.println(Outer.MSG);
}
}
}
这个时候的Inner类是一个独立类,如果此时要想实例化Inner类对象,只需要根据“ 外部类.内部类 ”的结构实例化对象即可
格式如下 外部类.内部类 内部类对象 = new 外部类.内部类();
这个时候类名称带有“.”
我们现在实例化static内部类对象
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.print();
}
}
输出结果是“haha”
所以以后如果发现类名称上提供有“.”,首先应该立刻想到这是一个内部类的结构,如果可以直接进行实例化,则应该立刻认识到这是一个static定义的内部类,但是static定义内部类的形式来讲并不常用,static定义内部接口的形式最为常用。
3.2 static定义内部接口
上代码,注意内部接口IMessage和IChannel
public interface IMessageWarp {
static interface IMessage{
public String getContent();
}
static interface IChannel{
public boolean connect(); //消息的发送通道
}
//消息发送
public static void send(IMessage msg,IChannel channel){
if(channel.connect()){
System.out.println(msg.getContent());
}else{
System.out.println("消息通道无法建立");
}
};
}
public class DefaultMessage implements IMessageWarp.IMessage {
@Override
public String getContent() {
return "哈哈";
}
}
public class NetChannel implements IMessageWarp.IChannel{
@Override
public boolean connect() {
return true;
}
}
public class Test {
public static void main(String[] args) {
IMessageWarp.send(new DefaultMessage() , new NetChannel());
}
}
之所以使用static定义的内部接口,主要是因为这些操作时属于一组相关的定义,有了外部接口之后可以更加明确的表述出这些接口的主要目的
4.方法中定义内部类
内部类可以在任意的结构中定义,这就包括了:类中、方法中、代码块中。但在方法中定义内部类的情况比较多。
public class Outer {
private String msg = "haha";
public void fun (long time){
class Inner { //内部类
public void print(){
System.out.println(Outer.this.msg);
System.out.println(time);
}
}
new Inner().print(); //方法中直接实例化内部类对象
}
}
public class Test {
public static void main(String[] args) {
new Outer().fun(2390239023L);
}
}
输出: haha 和 2390239023
此时在fun()方法内部提供有Inner内部类的定义,并且发现内部类可以直接访问外部类中的私有属性也可以直接访问方法中的参数,但对于方法中的参数直接访问时从JDK1.8开始支持的。而在JDK1.8之前,如果方法中定义的内部类要想访问方法中的参数则参数前必须追加final。
之所以取消这样的限制是为了扩展函数式编程准备的
5.匿名内部类
匿名类是一种简化的内部类的处理形式,其主要是在抽象类和接口的子类上使用的。
接口和抽象类是一样的
public interface IMessage {
public void send(String str);
}
public class MessageImpl implements IMessage {
@Override
public void send(String str) {
System.out.println(str);
}
}
public class Test {
public static void main(String[] args) {
IMessage msg = new MessageImpl();
msg.send("哈哈");
}
}
如果说现在IMessage接口中的MessageImpl子类只使用唯一的一次,那么是否还有必要将其定义为单独的类?那么在这样的要求下就发现这个时候定义的子类是有些多余了,所以就可以利用内部类的形式来解决此问题
public interface IMessage {
public void send(String str);
}
public class Test {
public static void main(String[] args) {
IMessage msg = new IMessage() {
@Override
public void send(String str) { //匿名内部类
System.out.println(str);
}
};
msg.send("哈哈");
}
}
匿名内部类不一定要在抽象类或接口上,但只有在抽象类和接口上才有意义。有些时候为了更加方的体现出匿名内部类的使用,往往可以利用静态方法做一个内部类的匿名内部类实现
在接口中直接定义匿名内部类
public interface IMessage {
public void send(String str);
}
public interface IMessage {
public void send(String str);
public static IMessage getInstance(){
return new IMessage() {
@Override
public void send(String str) {
System.out.println(str);
}
};
}
}
public class Test {
public static void main(String[] args) {
IMessage.getInstance().send("哈哈");
}
}
与内部类相比匿名内部类只是一个没有名字的只能够使用一次的,并且结构固定的一个子类。
总结:
1.方法,类,抽象类,接口,代码块中都可以定义内部结构-------类,抽象类,接口(当然普通类中不能定义抽象类)
2.匿名类是一种简化的内部类的处理形式,其主要在抽象类和接口的子类上使用
3.用的比较多的:
1).方法中定义内部类(内部类可以直接访问外部类中的私有属性和方法中的参数)
3).static定义内部接口(使用static定义的内部接口,主要是因为这些操作时属于一组相关的定义,有了外部接口之后可以更加明确的表述出这些接口的主要目的)
2).定义了一个接口,那么可以在内部利用类实现该接口