Java内部类详解

内部类总结

一个类的内部又完整的嵌套了另一个类的结构,被嵌套的类称为内部类,嵌套它的类称为外部类

类的五大成员:

  • 属性

  • 方法

  • 构造器

  • 代码块

  • 内部类

基本语法

class Outer{
    class Inner{
        
    }
}
class Other

内部类的分类:

  • 定义在外部类局部位置上(方法内、代码块内)

    • 局部内部类(有类名)
    • 匿名内部类(无类名)
  • 定义在外部类的成员位置上

    • 成员内部类(无static修饰)
    • 静态内部类(有static修饰)

局部内部类的使用

定义在方法中或者代码块里,作用域在方法体或者代码块中,本质仍是一个类
  • 可以直接访问外部类的所有成员,包括私有成员
  • 不能添加访问修饰符,因为它的地位与一个局部变量相同,局部变量是不可以用修饰符的。但是与局部变量相同,可以用final 修饰
  • 作用域:仅仅在定义它的方法或者代码块中
  • 局部内部类访问外部类的成员可以直接访问,而外部类访问局部内部类的成员需要创建对象再访问,且必须在作用域内

使用示例:

package com.innerclass_;

public class Test01 {
    public static void main(String[] args) {
        Outer outer = new Outer(101);
        outer.m1();
        outer.inner01.speak();
    }
}

class Outer{
    class Inner01{//内部类,在Outer类的内部
        public void speak(){
            System.out.println("Inner01中的n1:" + n1);
        }
    }
    Inner01 inner01 = new Inner01();
    //作用域仅在定义它的方法中
    private int n1 = 100;
    public void m1(){
        System.out.println("方法m1");
        //可以用final修饰
        final class Inner02{//局部内部类
            //可以直接访问外部类的所有成员
            public void f1(){
                System.out.println("Inner02中的n1:"+n1);
                System.out.print("Inner02中的:");
                m2();
            }
        }
        Inner02 inner02 = new Inner02();
        //在方法中使用内部类需要创建Inner02对象,然后调用内部类方法即可
        inner02.f1();
    }
    private void m2(){
        System.out.println("方法m2");
    }
    {
        System.out.println("Outer代码块");
    }

    public Outer(int n1) {
        System.out.println("Outer构造器");
        setN1(n1);
    }

    public int getN1() {
        return n1;
    }

    public void setN1(int n1) {
        this.n1 = n1;
    }
}

//输出:
Outer代码块
Outer构造器
方法m1
Inner02中的n1:101
Inner02中的:方法m2
Inner01中的n1:101
  • 外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
  • 如果外部类和局部内部类的成员重名,默认遵守就近原则,如果想访问外部类的成员,可以使用外部类名.this.成员去访问。
    示例:
    public class Test01 {
        public static void main(String[] args) {
            Outer outer = new Outer(101);
            outer.inner01.getName();
            outer.inner01.getInner01();
            outer.inner01.sayHi();
            outer.inner01.getOuterSayHi();
        }
    }
    
    class Outer{
        class Inner01{//内部类,在Outer类的内部
            private String name = "yll";
            public void speak(){
                System.out.println("Inner01中的n1:" + n1);
            }
    
            public void getName(){
                System.out.println("Inner01中的成员name:"+ name);
                //Outer本质是外部类的对象,即哪个对象调用了m1,Outer.this就是哪个对象
                System.out.println("Inner02中的成员name:"+Outer.this.name);
            }
    
            public void getInner01(){
                System.out.println(inner01);
            }
    
            public void sayHi(){
                System.out.println("Inner01 say hi.");
            }
    
            public void getOuterSayHi(){
                System.out.print("Inner01中访问sayHi:");
                Outer.this.sayHi();
            }
        }
        Inner01 inner01 = new Inner01();
        //作用域仅在定义它的方法中
        //Inner02 inner02 = new Inner02();
        private String name = "mxy";
        public void sayHi(){
            System.out.println("Outer say hi");
        }
    }
    
    //输出:
    Outer代码块
    Outer构造器
    Inner01中的成员name:yll
    Inner02中的成员name:mxy
    com.innerclass_.Outer$Inner01@1b6d3586
    Inner01 say hi.
    Inner01中访问sayHi:Outer say hi
    

匿名内部类

匿名内部类是定义在外部类的局部位置中的类,且没有类名

new 类或接口(参数列表){
    类体
};
  • 本质是类,且是内部类
  • 内部类没有名字(在JVM中有,但被隐藏起来了)
  • 内部类同时也是一个对象

匿名内部类解决的问题:

    //基于接口的匿名内部类
    //需求:想使用接口A,并创建对象,且此对象只使用一次,以后不再使用
    //传统方式:写一个类实现接口,并创建对象使用
    //不足之处:
    // 假如需要用许多调用不同的cry方法的对象话需要创建许多类,不方便
    //        A a = new Tiger();
//        a.cry();
//        a = new Cat();
//        a.cry();
        //由此引出匿名类

例子(基于接口的匿名内部类):

public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.method();
    }
}

class Outer2{
    private int n1 = 10;
    public void method(){
        //基于接口的匿名内部类
        //需求:想使用接口A,并创建对象,且此对象只使用一次,以后不再使用
        //传统方式:写一个类实现接口,并创建对象使用
        //不足之处:
        // 假如需要用许多调用不同的cry方法的对象话需要创建许多类,不方便
//        A a = new Tiger();
//        a.cry();
//        a = new Cat();
//        a.cry();
        //由此引出匿名类,使用匿名类简化开发
        A tiger = new A(){
            @Override
            public void cry() {
                System.out.println("Tiger cry~");
            }
        };
        //tiger的运行类型是什么?就是匿名内部类
        //底层:
//        class XXXX implements A{
//
//            @Override
//            public void cry() {
//                System.out.println("Tiger cry~");
//            }
//        }
        //可以根据此方法来查看类名:(类名为外部类加$加序号)
        System.out.println("tiger的运行类型="+tiger.getClass());
        //输出为tiger的运行类型=class com.anonymous_.Outer2$1
        //new 表示在底层创建了匿名类Outer2$1后立马就创建了实例,并把地址返回给tiger
        //匿名类不能重复使用,但是对象可以
        tiger.cry();
    }
}
interface A{
    public void cry();
}

class Tiger implements A{

    @Override
    public void cry() {
        System.out.println("Tiger Cry~");
    }
}

class Cat implements A{

    @Override
    public void cry() {
        System.out.println("Cat meow meow~");
    }
}

基于类的匿名内部类:

class Outer3{
    public void sayHi(){
        //基于类的匿名内部类
        //father的编译类型:Father
        //运行类型:Outer2$2
        //不带大括号运行类型就是Father
        //注意("mxy")会传递给Father构造器,但是不能重写Father构造器
        Father father = new Father("mxy"){
            @Override
            public void speak() {
                System.out.println("内部匿名类重写了speak方法");
            }
        };
        //输出:class com.anonymous_.Outer2$1
        System.out.println("father对象的运行类型:"+ father.getClass());
    }
}

class Father{
    private String name;
    
    public void speak(){
        System.out.println("类Father  speak");
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Father(String name) {
        setName(name);
    }
}

基于抽象类的匿名内部类:

抽象类的匿名类必须实现,原因与抽象类的子类必须实现相同
public class AnonymousTest {
    public static void main(String[] args) {
        Outer.play();
    }
}

class Outer{

    public static void play(){
        Animal animal = new Animal() {
            //必须实现
            @Override
            public void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}

abstract class Animal{
    public abstract void eat();
}

匿名内部类细节:

  • 匿名内部类即是类的定义,其本身也是一个对象
  • 可以直接调用
    class Outer{
    
        public static void play(){
            new Animal() {
                @Override
                public void eat() {
                    System.out.println("小猫吃鱼...");
                }
            }.eat();
            //只调用一次eat方法可以这么写
        }
    }
    
  • 可以访问外部类的所有成员,包括私有的
  • 不能添加访问修饰符,因为其地位就是局部变量
  • 外部其他类不能访问匿名内部类
  • 若外部类和匿名内部类的成员重名,匿名内部类访问时遵循就近原则,若非要访问外部类成员,可以用 外部类名.this.成员去访问

匿名内部类的实践:

  • 当作实参直接传递,简洁高效
    public class Use {
        public static void main(String[] args) {
            show(new AA(){
    
                @Override
                public void cry() {
                    System.out.println("I'm crying...");
                }
            });
        }
        public static void show(AA a){
            a.cry();
        }
    }
    //若不使用匿名内部类,需要对接口进行实现,然后再new对象传参,很麻烦(硬编码)
    interface AA{
        public void cry();
    }
    

成员内部类

成员内部类定义在外部类的成员位置,并且无static修饰

注意:

  • 可以访问外部类所有成员,包括私有的
    public class Test1 {
        public static void main(String[] args) {
            Outer outer = new Outer();
            outer.t1();
        }
    }
    class Outer{
        private int n1 = 10;
        class Inner{
            public void say(){
                System.out.println("Outer的n1:"+n1);
            }
        }
    
        public void t1(){
            Inner inner = new Inner();
            inner.say();
        }
    }
    
  • 可以添加任意的访问修饰符(public protected 默认 private),因为其地位就是一个成员
  • 作用域:
    • 与外部类其他成员一致,为整个类体
    • 成员内部类访问外部类可以直接访问
    • 外部类访问成员内部类需要先创建再访问
    • 外部其他类访问成员内部类(可以的),访问方式如下:
      • 创建外部对象后new来访问
        public class Test1 {
            public static void main(String[] args) {
                Outer outer = new Outer();
                //1、
                Outer.Inner inner = outer.new Inner();
                //new Inner();Inner是成员 因此需要先创建Outer后再创建
            }
        }
        
      • 在外部类中创建一个返回new内部类的方法,通过调用外部类的方法来访问
        public class Test1 {
            public static void main(String[] args) {
                Outer outer = new Outer();
                outer.t1();
                //2、
                Outer.Inner inner1 = outer.getInner();
            }
        }
        
  • 如果外部类和内部类的成员重名时,内部类访问会遵顼就近原则, 因此若想访问外部类的成员,需要(外部类名.this.成员)去访问

静态内部类

定义在外部类的成员位置,有static修饰

注意:

  • 可以直接访问外部类的所有静态成员,包括私有的,但是不能访问非静态成员
  • 可以添加任意访问修饰符,因为其地位就是一个成员
  • 作用域:同其他的成员一样,为整个类体
    public class Test1 {
        public static void main(String[] args) {
            Outer outer = new Outer();
            outer.useInner();
        }
    }
    
    class Outer{
        private static int n1 = 1000;
        private double nt = 99.99;
        public static class Inner{
            public void say(){
                System.out.println("Outer类中的n1:"+n1);
                //System.out.println("Outer类中的nt"+ nt);
                //不可访问非静态成员
            }
        }
        public void useInner(){
            Inner inner = new Inner();
            inner.say();
        }
    }
    
  • 访问:
    • 内部类可以访问外部类所有的静态成员,但是不能访问非静态成员
    • 外部类访问内部类可以通过类名.静态成员来访问,或创建内部类访问其非静态成员
    • 外部其他类访问内部类的方法:
      • 1、由于是静态内部类,可以直接通过类名访问(在访问权限内)
        public class Test1 {
            public static void main(String[] args) {
                //外部其他类使用Inner不需要新建Outer
                Outer.Inner inner = new Outer.Inner();
            }
        }
        
      • 2、编写一个方法,返回静态内部类(尽量写为静态方法)
        public class Test1 {
            public static void main(String[] args) {
                //静态方法返回Inner
                Outer.Inner inner1 = Outer.getInner();
            }
        }
        
  • 重名:依然遵守就近原则,若要访问外部类,直接用外部类名.成员去访问(只能是静态成员)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值